Railway Operation Simulator  v2.9.1
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */{
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
229  {
231  }
232  if(FrontCodePtr == 0)
233  {
234  throw Exception("Error in attempting to delete FrontCodePtr");
235  }
236  delete FrontCodePtr;
237  FrontCodePtr = 0;
238  for(int x = 0; x < 4; x++)
239  {
240  if(BackgroundPtr[x] == 0)
241  {
242  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
243  }
244  delete BackgroundPtr[x];
245  BackgroundPtr[x] = 0;
246  }
247  for(int x = 0; x < 4; x++)
248  {
249  if(HeadCodeGrPtr[x] == 0)
250  {
251  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
252  }
253  delete HeadCodeGrPtr[x];
254  HeadCodeGrPtr[x] = 0;
255  }
256  Utilities->CallLogPop(649);
257 }
258 
259 // ---------------------------------------------------------------------------
260 
262 /*
263  Plots the train starting position on screen. Note that the check for starting on straight points &
264  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
265  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
266  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
267  Set the headcode graphics pointers from the headcode text, then check whether starting at a
268  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
269  for the continuation element. Otherwise set Lead and Mid values,
270 
271  and Lead element value unless
272  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
273  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
274  then check if a train on either Mid or Lag and if so give a warning message and return false so
275  that the calling function can delete the train. Plot the Mid element train values then do similarly
276  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
277  the train. Finally set the Plotted flag and return true.
278 */{
279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
280  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
281 
283  // PlotStartTime = TrainController->TTClockTime;
284  FirstHalfMove = true;
285 
286  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
287  // 'claim' it for this train to prevent any other waiting trains trying to enter
289  {
290  LagElement = -1; // not to be plotted
291  LagExitPos = 0; // not to be plotted
292  LagEntryPos = 0; // not to be plotted
293  MidElement = -1; // not to be plotted
294  MidExitPos = 0; // not to be plotted
295  MidEntryPos = 0; // not to be plotted
297  LeadExitPos = 1; // will be 1 for continuation entry
298  LeadEntryPos = 0;
299 
301  MaxExitSpeed = StartSpeed; // initial value
303  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
304  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
305  if(EntrySpeed > SpeedLimit)
306  {
307  EntrySpeed = SpeedLimit;
308  }
310  {
312  }
314  // LeadElement is the element to be entered
315 
316  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
317  // can achieve ExitSpeedFull at the half braking rate.
319  {
320  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
321  if(TempEntrySpeed < EntrySpeed)
322  {
323  EntrySpeed = TempEntrySpeed;
325  }
326  }
327  Straddle = MidLag; // only for starting on a continuation
329  // no need to stop gap flashing if start on continuation
330  }
331  else // not starting at a continuation
332  {
333  LagElement = -1;
334  LagEntryPos = 0;
335  LagExitPos = 0;
342 
344  MaxExitSpeed = StartSpeed; // initial value
346  bool TempDerail = false; // dummy
347  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
349  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
350  {
351  StoppedWithoutPower = true;
352  }
353  // facing buffers check - ignore starting speed if start facing buffers
354  StoppedAtBuffers = false;
355  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
358  {
359  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
360  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
361  EntrySpeed = 0;
362  ExitSpeedHalf = 0;
363  ExitSpeedFull = 0;
364  MaxExitSpeed = 0;
365  // SetTrainMovementValues not called so set this here
366  BrakeRate = 0;
369  StoppedAtSignal = false;
370  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
371  // signal check is an 'else'
372  if(!StoppedAtLocation)
373  {
374  StoppedAtBuffers = true; // stopped at location takes precedence
375  }
376  }
377 
378  // facing continuation check - don't allow to stop even if no power
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
386  BrakeRate = 0;
387  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
388  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
389  }
390 
391  // Signal check
392  else if((NextElementPosition > -1) && (NextEntryPos > -1))
393  // condition check added as precaution after SloughIECC error reported by James U
394  {
395  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
396  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
397  {
398  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
399  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
400  EntrySpeed = 0;
401  ExitSpeedHalf = 0;
402  ExitSpeedFull = 0;
403  MaxExitSpeed = 0;
404  BrakeRate = 0;
407  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
408  {
409  StoppedAtSignal = true;
411  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
412  }
414  {
415  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
416  // forwards, but don't change the background colour so still shows as stopped at location
417  StoppedAtSignal = true;
418  }
419  }
420  else
421  {
422  StoppedAtSignal = false;
423  if(NextEntryPos > 1)
424  {
425  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
426  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
427  }
428  else
429  {
430  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
431  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
432  }
433  if(EntrySpeed > SpeedLimit)
434  {
435  EntrySpeed = SpeedLimit;
436  }
438  {
440  }
442  TDateTime TestTime = TrainController->TTClockTime; // test
443  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
444  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
445  // NextElement is the element to be entered
446 
447  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
448  // can achieve ExitSpeedFull at the half braking rate.
450  {
451  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
452  // half braking
453  if(TempEntrySpeed < EntrySpeed)
454  {
455  EntrySpeed = TempEntrySpeed;
456  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
457  }
458  }
459  }
460  }
462  {
463  throw Exception("Error, LeadElement Exit Connection is NotSet");
464  }
465  }
466  if(MidElement > -1) // will be -1 if start on continuation
467  {
468  Straddle = LeadMid;
472  {
473  for(int x = 0; x < 4; x++)
474  {
475  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
476  }
477  }
478  else
479  {
480  for(int x = 0; x < 4; x++)
481  {
483  }
484  }
485  if(TrainMode == Timetable)
486  {
488  }
489  else
490  {
492  }
494  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
495 
498 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
499  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
500  {
501  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
502  Utilities->CallLogPop(651);
503  return false;
504  }
505 */
510  PlotTrainGraphic(8, 0, Display);
511  PlotTrainGraphic(9, 1, Display);
512 
515 
516  // pick up background bitmaps [2] & [3]
517 
520 
521  PlotElement[2] = MidElement;
523  PlotElement[3] = MidElement;
525  PlotTrainGraphic(10, 2, Display);
526  PlotTrainGraphic(11, 3, Display);
527  // Plotted = true; set in PlotTrainGraphic
528  }
529  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
530  Utilities->CallLogPop(652);
531 }
532 
533 // ---------------------------------------------------------------------------
534 void TTrain::UnplotTrain(int Caller)
535 {
536  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
537  if(!Plotted)
538  {
539  return;
540  }
541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
542 
543  if(Straddle == MidLag)
544  {
545  if(MidElement > -1)
546  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(LagElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  }
562  else if(Straddle == LeadMidLag)
563  {
564  if(LeadElement > -1)
565  {
568  // to force plot of locked route marker, needed once only for the element
569  }
570  if(MidElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(LagElement > -1)
579  {
582  // to force plot of locked route marker, needed once only for the element
583  }
584  }
585  else if(Straddle == LeadMid)
586  {
587  if(LeadElement > -1)
588  {
593  // to force plot of locked route marker, needed once only for the element
594  }
595  if(MidElement > -1)
596  {
601  // to force plot of locked route marker, needed once only for the element
602  }
603  }
604  if(LeadElement > -1)
605  {
607  }
608  if(MidElement > -1)
609  {
611  }
612  if(LagElement > -1)
613  {
615  }
616  Plotted = false;
618  Display->Update();
619  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
620  // resurrected when Update() dropped from PlotOutput etc
621  Utilities->CallLogPop(653);
622 }
623 
624 // ----------------------------------------------------------------------------
625 
626 void TTrain::UpdateTrain(int Caller)
627 /*
628  Note: Some changes made since comments written
629 
630  Brief:
631  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
632  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
633  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
634  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
635  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
636  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
637  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
638  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
639  changed to MidLag within the function and all elements moved down one, old Mid becomes
640  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
641  incremented to reflect the position the train now occupies.
642 
643  Detail:
644  Set TrainFailurePending if all conditions met
645  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
646  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
647  and return.
648  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
649  If there's a LagElement (there will be but include check for good practice - next
650  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
651  train fully on offending point - Derail set and DerailPanding reset, train background
652  colour changed (note that BackgroundColour is a property of the train itself) then return.
653  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
654  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
655  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
656  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
657  if LeadElement is a fouled trailing point.
658  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
659  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
660  replotting the last background segment and checking whether the element is a bridge or crossover with the other
661  track in a route, in which case the route colour is replotted.
662  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
663  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
664  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
665  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
666  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
667  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
668  train can be deleted by the calling function, and the function returns.
669  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
670  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
671  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
672  basic red aspect.
673 
674  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
675  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
676  regardless of direction, and with the correct front code colour.
677 
678  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
679  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
680  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
681  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
682  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
683  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
684  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
685 
686  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
687  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
688  changed similarly. The function then returns.
689 
690  If Crashed is not set then Straddle is incremented and the function returns.
691 */
692 
693 {
694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
695  UpdateCounter++;
696  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
697  if(UpdateCounter >= 100)
698  {
699  UpdateCounter = 0;
700  }
701  int RandRange = (TrainController->MTBFHours * 3600) / 53;
702 
703  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
704  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
705  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
706  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
707  // RandomFailureCounter value is fixed for a full cycle of train updates so this
708  // makes sure there's no bunching of failures as there is for a fixed comparison number
709  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
710  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
711  {
712  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
713  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
714  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
715  // don't fail if:
716  // (a) on a continuation (entering or leaving);
717  // (b) already failed;
718  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
719  // (d) train terminated;
720  // (e) crashed or derailed; or
721  // (f) under signaller control and stopped.
722  {
723  if((random(RandRange)) == 0)
724  // max value for RandRange is over 2x10^9
725  {
726  // here if failure due
727  TrainFailurePending = true;
728  // fail when PlotElements set to proper Lead, Mid & Elements
729  }
730  }
731  }
732 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
733  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
734  {
735  StoppedWithoutPower = true;
736  }
737 */
738  int LockedVectorNumber;
739  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
740  // default values - these needed for route checker below
741  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
742 
744  {
746  }
747  if(Crashed || Derailed)
748  {
750  {
751  PlotTrain(7, Display);
752  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
753  Display->Update();
754  }
755  OpTimeToAct = 0.0;
756  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
757  Utilities->CallLogPop(1017);
758  return; // no further action, user has to remove or work around
759  }
761  {
763  }
765  {
767  }
769  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
770  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
771  // to move & then stop again at the same station
772  {
773  TimeTimeLocArrived = false;
774  }
775  if(!Stopped() && !SPADFlag && !TrainFailed)
776  {
778  }
779  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
780  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
781 /* old version where force a stop at buffers regardless of speed
782  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
783  else StoppedAtBuffers = false;
784 */
785 
786  // new version where crash if run into buffers
787  if(!Crashed)
788  {
789  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
790  {
791  if(ExitSpeedFull > 1)
792  {
793  Crashed = true;
797  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
798  // no need for missed action logs - will be sent when train removed
799  StoppedAtBuffers = false;
800  }
802  // stopped at location & stopped without power take precedence
803  {
804  StoppedAtBuffers = true;
805  }
806  else
807  {
808  StoppedAtBuffers = false;
809  }
810  }
811  else
812  {
813  StoppedAtBuffers = false;
814  }
815  }
816  else
817  {
818  StoppedAtBuffers = false;
819  }
820  // if crashed don't want stopped at buffers set
821 
822  // also crash if run into a level crossing that is changing or has barriers up
823  if(!Crashed)
824  {
825  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
826  {
827  int H = Track->TrackElementAt(873, LeadElement).HLoc;
828  int V = Track->TrackElementAt(874, LeadElement).VLoc;
829  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
830  {
831  Crashed = true;
835  // no need for missed action logs - will be sent when train removed
836  }
837  }
838  }
840  {
842  }
843  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
844  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
845  {
846  HoldAtLocationInTTMode = true;
847  }
848  else if(TrainMode == Timetable)
849  {
850  HoldAtLocationInTTMode = false;
851  }
852  // in Signaller mode HoldAtLocationInTTMode not changed
853 
854  // check if departure pending & set times unless already set
855  if(TrainMode == Timetable)
856  {
858  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
859  {
860  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
861  {
863  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
864  {
865  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
866  }
867  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
868  DepartureTimeSet = true;
869  }
870  }
871  }
873  {
874  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
875  // calculate every 1 sec (in real time, not timetable time) for all trains
876  }
877  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
878  if(TrainMode == Timetable)
879  {
880  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
881  {
882  RemainHereLogNotSent = true;
883  }
885  {
886  // ignore TimeLoc & TTLoc departures
887  // Action logs given in functions
889  LastActionTime + TDateTime(30.0 / 86400)))
890  {
891  if(ActionVectorEntryPtr->Command == "fsp")
892  {
893  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
894  FrontTrainSplit(0);
895  if(TrainFailurePending) // ok, stopped so PlotElements set
896  {
897  TrainHasFailed(0);
898  }
899  Utilities->CallLogPop(2041);
900  return;
901  }
902  else if(ActionVectorEntryPtr->Command == "rsp")
903  {
904  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
905  RearTrainSplit(0);
906  if(TrainFailurePending) // ok, stopped so PlotElements set
907  {
908  TrainHasFailed(1);
909  }
910  Utilities->CallLogPop(2042);
911  return;
912  }
913  else if(ActionVectorEntryPtr->Command == "Fjo")
914  {
915  FinishJoin(0);
916  }
917  else if(ActionVectorEntryPtr->Command == "jbo")
918  {
919  JoinedBy(0);
920  }
921  else if(ActionVectorEntryPtr->Command == "cdt")
922  {
924  }
925  else if(ActionVectorEntryPtr->Command == "Fns")
926  {
927  NewTrainService(0);
928  }
929  else if(ActionVectorEntryPtr->Command == "Frh")
930  {
931  RemainHere(0);
932  }
933  else if(ActionVectorEntryPtr->Command == "Fer")
934  {
935  TimetableFinished = true;
936  }
937  // other aspects of 'Fer' dealt with in TTrain::Update()
938  else if(ActionVectorEntryPtr->Command == "F-nshs")
939  {
941  }
942  else if(ActionVectorEntryPtr->Command == "Frh-sh")
943  {
945  }
946  else if(ActionVectorEntryPtr->Command == "Fns-sh")
947  {
949  }
950 /*
951  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
952  shuttle headcode (no train creation)
953  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
954  remain here
955  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
956  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
957 */
958  }
959  }
960  else
961  {
963  {
965  }
966  }
967  }
968  if(TrainMode == Timetable)
969  {
970  if(StoppedAtBuffers)
971  {
972  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
973  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
974  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
975  if(BufferLocation == "")
976  {
978  }
979  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
980  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
981  {
985  {
987  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
988  // Drop missed actions so user can still use sig mode to get back on track
990  }
991  if(TrainFailurePending) // ok, stopped so PlotElements set
992  {
994  TrainHasFailed(2);
995  }
996  Utilities->CallLogPop(1020);
997  return;
998  }
999  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1000  ReleaseTime))
1001  {
1004  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(3);
1015  }
1016  Utilities->CallLogPop(1397);
1017  return;
1018  }
1019  }
1020  else
1021  {
1023  }
1024  }
1025  else
1026  {
1028  }
1029  if(TrainMode == Timetable)
1030  {
1032  {
1034  }
1036  {
1038  }
1039  }
1040  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1041  // restart after stopped for train in front
1042  int NextElementPosition, NextEntryPos;
1043 
1044  if(LeadElement > -1) // if an exit continuation then not set
1045  {
1046  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1047  {
1049  }
1050  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1051  {
1052  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1053  {
1054  LeadExitPos = 1;
1055  }
1056  else
1057  {
1058  LeadExitPos = 3;
1059  }
1060  }
1061  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1062  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1063  }
1064  else
1065  {
1066  NextElementPosition = -1;
1067  NextEntryPos = -1;
1068  }
1069  if((NextElementPosition > -1) && (NextEntryPos > -1))
1070  // may be buffers or continuation so need this check
1071  {
1072 /*
1073  Check whether calling-on conditions met:-
1074  a) approaching train has stopped at a signal but not at a location;
1075  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1076  change of direction (cdt), remaining here (Frh), or under signaller control);
1077  c) at least 1 platform available for the approaching train;
1078  d) points (if any) set for direct route into platform;
1079  e) approaching train is to stop at station;
1080  f) no more facing signals between train and platform;
1081  g) [dropped g]
1082  h) train in front preventing route being set far enough to release stop signal;
1083  i) train in front not exiting at continuation;
1084  j) signal must be within 4km of the stop platform;
1085  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1086  l) no existing route conflicts with the route into the platform.
1087  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1088  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1089 */
1090  if(TrainMode == Timetable)
1091  {
1092  if(CallingOnAllowed(0))
1093  {
1094  CallingOnFlag = true;
1095  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1096  }
1097  else
1098  {
1099  if(CallingOnFlag)
1100  {
1102  }
1103  CallingOnFlag = false;
1104  }
1105  }
1106  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1107  {
1108  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1109  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1110  // sets StoppedAtSignal again & train doesn't move
1111  StoppedAtSignal = false;
1112  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1113  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1114  // LeadMidLag and front of train was on LeadElement (after the current move)
1116  EntrySpeed = 0;
1118  FirstHalfMove = true;
1119  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1120  // NextElement is the element to be entered
1121  }
1123  {
1124  if(ClearToNextSignal(0))
1125  {
1126  StoppedForTrainInFront = false;
1127  BeingCalledOn = false;
1128  EntrySpeed = 0;
1130  FirstHalfMove = true;
1131  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1132  }
1133  else
1134  {
1135  if(TrainFailurePending) // ok, stopped so PlotElements set
1136  {
1137  TrainHasFailed(4);
1138  }
1139  Utilities->CallLogPop(1097);
1140  return;
1141  }
1142  }
1143  }
1144  if((Straddle == MidLag) && (LeadElement != -1))
1145  // later check only for Straddle == LeadMid, so need this check here for initial train start
1146  {
1148  }
1149 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1150  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1151  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1152  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1153  which could be when start as Snt.
1154  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1155  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1156  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1157  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1158  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1159  reached.
1160  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1161  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1162  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1163  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1164  sending a message to the performancelog.
1165 */
1166 
1167  if(TrainMode == Timetable)
1168  {
1170  {
1171  if(BeingCalledOn)
1172  {
1173  StoppedForTrainInFront = true;
1174  }
1176  {
1178  }
1180  {
1181  // value updated at every scheduled departure & arrival
1183  AnsiString StationName;
1185  {
1187  }
1189  {
1191  }
1192  else
1193  {
1194  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1195  }
1196  EntrySpeed = 0;
1200  FirstHalfMove = true;
1201  StoppedAtLocation = false;
1202 
1203  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1204  {
1205  StoppedWithoutPower = true;
1206  }
1207  if((NextElementPosition > -1) && (NextEntryPos > -1))
1208  // condition check added for SloughIECC error reported by James U
1209  {
1210  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1211  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1212  {
1213  StoppedAtSignal = true;
1214  if(!StoppedWithoutPower)
1215  // if stopped without power just keep existing background colour
1216  {
1218  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1219  }
1220  }
1221  }
1223  {
1224  TimeTimeLocArrived = false;
1225  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1226  // no warning for TimeTimeLoc departure
1227  }
1228  else
1229  {
1231  }
1232  DepartureTimeSet = false;
1233  // no need to set LastActionTime for a departure
1235  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1236  // note
1237 /*
1238  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1239  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1240  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1241  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1242  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1243  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1244  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1245 */
1247  {
1248  StoppedAtBuffers = true;
1249  }
1250  else if(!StoppedWithoutPower)
1251  // if buffers or no power, don't set values
1252  {
1254  {
1255  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1256  // NextElement is the element to be entered
1257  }
1258  else
1259  {
1261  // use LeadElement for an exit continuation
1262  }
1263  }
1264  }
1265  }
1266  }
1267  if(Straddle == LeadMidLag)
1268  {
1270  {
1271  Utilities->CallLogPop(654);
1272  return;
1273  }
1274  }
1275  else
1276  {
1278  {
1279  Utilities->CallLogPop(655);
1280  return;
1281  }
1282  }
1283  if((LeadElement > -1) && (MidElement > -1))
1284  {
1286  {
1287  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1288  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1289  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1290  SignallerStoppingFlag = false;
1291  StepForwardFlag = false;
1292  }
1293  }
1294  if(Stopped())
1295  // this is what prevents another movement if the train is stopped
1296  {
1297  if(TrainFailurePending) // ok, stopped so PlotElements set
1298  {
1299  TrainHasFailed(5);
1300  }
1301  BrakeRate = 0;
1302  Utilities->CallLogPop(656);
1303  return;
1304  }
1305  // here when ready for next move
1306 
1307  // check for train in front & if so stop at next access (when train fully on element next to train)
1308  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1309  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1310  // variable TrainInFrontInSignallerModeFlag
1311  {
1312  if(LeadElement > -1)
1313  {
1314  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1315  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1316  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1317  // true if another train on NextEntryPos track whether bridge or not
1318  {
1319  StoppedForTrainInFront = true;
1320  }
1321  else
1322  {
1323  StoppedForTrainInFront = false;
1324  }
1325  }
1326  }
1327  if((Straddle == LeadMid) && SPADFlag)
1328  // give message + plot background when ready to move half past the signal
1329  {
1330  if(NextElementPosition > -1)
1331  {
1332  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1333  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1334  {
1335  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1337  // if goes past 2 signals then give message twice
1339  }
1340  }
1341  }
1342  if(Straddle == LeadMidLag)
1343  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1344  {
1345  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1346  if(SPADFlag)
1347  {
1348  if(ExitSpeedFull == 0)
1349  {
1350  StoppedAfterSPAD = true;
1351  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1352  }
1353  }
1355  {
1356  if(ExitSpeedFull == 0)
1357  {
1358  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1359  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1360  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1361  // is sent at the right time and once only.
1362  SignallerStopped = true;
1363  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1364  StepForwardFlag = false;
1365  SignallerStoppingFlag = false;
1366  TTrackElement TE;
1367  AnsiString Loc = "";
1368  bool LocNamed = false;
1369  if(LeadElement > -1)
1370  {
1371  TE = Track->TrackElementAt(782, LeadElement);
1372  if(TE.ActiveTrackElementName != "")
1373  {
1374  Loc = TE.ActiveTrackElementName;
1375  LocNamed = true;
1376  }
1377  else
1378  {
1379  Loc = "track element " + TE.ElementID;
1380  }
1381  }
1382  if((MidElement > -1) && !LocNamed)
1383  {
1384  TE = Track->TrackElementAt(783, MidElement);
1385  if(TE.ActiveTrackElementName != "")
1386  {
1387  Loc = TE.ActiveTrackElementName;
1388  LocNamed = true;
1389  }
1390  else if(Loc == "")
1391  {
1392  Loc = "track element " + TE.ElementID;
1393  }
1394  }
1395  if(Loc == "")
1396  {
1397  Loc = "outside railway";
1398  // must have stopped after left at a continuation (because both lead & mid == -1)
1399  }
1400  else
1401  {
1402  Loc = "at " + Loc;
1403  }
1404  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1405  }
1406  }
1407  if(LeadElement > -1) // if an exit continuation then not set
1408  {
1409  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1410  {
1412  }
1413  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1414  {
1415  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1416  {
1417  LeadExitPos = 1;
1418  }
1419  else
1420  {
1421  LeadExitPos = 3;
1422  }
1423  }
1424  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1425  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1426  }
1427  else
1428  {
1429  NextElementPosition = -1;
1430  NextEntryPos = -1;
1431  }
1434  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1435 
1436  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1437  {
1438  StoppedWithoutPower = true;
1439  }
1440  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1441  // may be buffers or continuation. SPADFlag added at v2.1.0
1442  // so don't override the SPAD colour & don't set StoppedAtSignal
1443  {
1444  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1445  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1446  {
1447  StoppedAtSignal = true;
1448  if(!StoppedWithoutPower)
1449  // leave background as is if no power, but set StoppedAtSignal
1450  {
1452  }
1453  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1454  }
1455  }
1456  if(!Stopped())
1457  {
1458  if((NextElementPosition > -1) && (NextEntryPos > -1))
1459  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1460  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1461  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1462  // function for fuller explanation
1463  {
1464  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1465  // NextElement is the element to be entered
1466  }
1467  // follow the continuation exits:-
1468  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1469  {
1471  // Use LeadElement for calcs if lead is a continuation
1472  }
1473  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1474  {
1476  // Use MidElement for calcs if mid is a continuation
1477  }
1478  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1479  {
1481  // Use LagElement for calcs if lag is a continuation
1482  }
1483  }
1484  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1485  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1486  // Trains may not be in a route
1487  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1488  {
1489  // NB if LeadElement == -1 then the above test returns false
1490  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1491  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1492  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1504  }
1505  }
1506  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1507  // Trains may not be in a route
1508  {
1509  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1510  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1511  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1512  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1513  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1514  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1515  FirstPair.second).GetELink() == TempELink))
1516  {
1517  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1518  }
1519  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1520  SecondPair.second).GetELink() == TempELink))
1521  {
1522  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1523  }
1524  }
1525  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1526  // Trains may not be in a route
1527  {
1528  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1529  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1530  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1531  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1532  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1533  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1534  FirstPair.second).GetELink() == TempELink))
1535  {
1536  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1537  }
1538  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1539  SecondPair.second).GetELink() == TempELink))
1540  {
1541  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1542  }
1543  AllRoutes->CheckMapAndRoutes(8); // test
1544  }
1545  if(LagElement > -1)
1546  // not entering at a continuation so can deal with train leaving the lag element
1547  {
1549  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1550  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1551  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1552 
1553  TPrefDirElement PrefDirElement;
1554  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1555  // as this is a 16x16 graphic
1557  {
1559  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1560  }
1561  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1562  {
1563  int RouteNumber;
1564  TrainGone = true;
1565  // flag to indicate train to be deleted - outside this function
1567  {
1568  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1569  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1570  // calc distance from & inc last signal to exit
1571  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1572  int NewLastElement = 0, NewLastExitPos = 0;
1573  // need above because can't change LastElement & LastExitPos until both new values obtained
1574  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1575  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1576  LastElement).TrackType != Points))
1577  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1578  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1579  // leave CumDistance as it was in these circumstances.
1580  {
1581  if(LastExitPos < 2)
1582  {
1583  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1584  }
1585  else
1586  {
1587  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1588  }
1589  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1590  if(NewLastElement == -1)
1591  // this will catch buffers or any other connection failure
1592  {
1593  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1594  }
1595  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1596  if(NewLastExitPos == -1)
1597  {
1598  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1599  }
1600  LastElement = NewLastElement;
1601  LastExitPos = NewLastExitPos;
1602  }
1603  // if at signal add this in too
1604  if(CumDistance < 1200)
1605  {
1606  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1607  }
1608  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1609  // else use 1200m - CumDistance
1610  int FirstDistance = 0;
1611  if(CumDistance >= 1200)
1612  {
1613  FirstDistance = 100;
1614  }
1615  else
1616  {
1617  FirstDistance = 1200 - CumDistance;
1618  }
1619  if(FirstDistance < 100)
1620  {
1621  FirstDistance = 100; // don't allow < 100
1622  }
1623  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1624  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1625  if(ExitSpeedFull > 20.0)
1626  {
1627  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1628  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1629  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1630  // 4320.0 = 3.6 * 1200, .0 to make it a double
1631  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1632  }
1633  else
1634  {
1635  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1636  ContinuationAutoSigEntry.SecondDelay = 120.0;
1637  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1638  }
1639  ContinuationAutoSigEntry.AccessNumber = 0;
1640  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1642  {
1644  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1645  VectorIT++)
1646  {
1647  if(VectorIT->RouteNumber == RouteNumber)
1648  {
1649  // another train has passed out of same route so erase earlier entry
1650  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1651  break;
1652  }
1653  }
1654  }
1655  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1656  }
1658  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1659  Display->Update();
1660  // need to keep this since Update() not called for PlotSmallOutput as too slow
1661  Utilities->CallLogPop(659);
1662  return;
1663  }
1664  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1665  if(LeadElement > -1)
1666  {
1668  // changed to lead so reset early
1669  {
1670  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1672  // don't plot if zoomed out
1673  if(!Display->ZoomOutFlag)
1674  {
1676  }
1677  // covers signal resetting in same direction
1678  }
1679  }
1681  {
1682  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1683  {
1684  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1685  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1686  TPrefDirElement PrefDirElement;
1687  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1689  {
1691  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1692  }
1694  LockedVectorNumber)))
1695  {
1697  }
1698  }
1699  }
1700  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1701  {
1702  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1704  // don't plot if zoomed out
1705  if(!Display->ZoomOutFlag)
1706  {
1708  }
1709  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1710  }
1712  {
1713  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1714  {
1715  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1716  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1717  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1718  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1719  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1720  int RouteNumber;
1722  // already know it's an autosigsroute, this is just to get the RouteNumber
1723  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1724  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1725  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1726  int RouteNumber2;
1728  // already know it's an autosigsroute, this is just to get the RouteNumber
1729  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1730  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1731  {
1732  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1733  // this was in the 1.3.0 addition but without the condition
1734  }
1735  // end of 1.3.2 addition
1736  // end of 1.3.0.addition
1737  }
1738  TPrefDirElement PrefDirElement;
1739  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1741  {
1743  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1744  }
1745  }
1746  }
1747  }
1748  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1749  if(Straddle == LeadMid)
1750  {
1751  AllowedToPassRedSignal = false;
1752  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1753  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1754  if(DerailPending)
1755  // set during last GetLeadElement, but only act on it when train fully on offending point
1756  // i.e. next time Straddle reaches LeadMid
1757  {
1758  Derailed = true;
1759  DerailPending = false;
1763  Utilities->CallLogPop(657);
1764  return;
1765  }
1772  Straddle = MidLag;
1773  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1774  // LeadElement during this function (note that if stopped at signal then won't get this far)
1775  if(LeadElement > -1)
1776  {
1778  // i.e an exit continuation only
1779  // if don't exclude entry continuations then can't progress past it
1780  {
1781  LeadElement = -1;
1782  }
1783  else
1784  {
1785  GetLeadElement(0);
1786  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1788  if(Stopped())
1789  {
1790  if(TrainFailurePending) // ok, stopped so PlotElements set
1791  {
1792  TrainHasFailed(6);
1793  }
1794  Utilities->CallLogPop(658);
1795  return; // i.e. don't move forward one step if next element is a red signal
1796  }
1797  }
1798  }
1799  }
1800  if(LagElement > -1)
1801  {
1802  // below are the actions required at both half moves for LagElement > -1
1804 
1805  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1806  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1807  // need to do this for each half element
1808 
1809  TPrefDirElement PrefDirElement;
1810  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1811  {
1812  int RouteNumber; // holder for call below - not used
1814  {
1815  if(Utilities->clTransparent == TColor(0xFFFFFF))
1816  // change to black for a white background
1817  {
1819  // only applies for AutoSigs Route in case was locked & timed out
1820  }
1821  else
1822  // change to white for a dark background
1823  {
1825  // only applies for AutoSigs Route in case was locked & timed out
1826  }
1828  }
1829  }
1831  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1832  // or a train on the opposite track - needs to be replotted
1833  }
1834  // update all array values
1835  HOffset[3] = HOffset[2];
1836  HOffset[2] = HOffset[1];
1837  HOffset[1] = HOffset[0];
1838  VOffset[3] = VOffset[2];
1839  VOffset[2] = VOffset[1];
1840  VOffset[1] = VOffset[0];
1841  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1842 
1843  BackgroundPtr[3] = BackgroundPtr[2];
1844  BackgroundPtr[2] = BackgroundPtr[1];
1845  BackgroundPtr[1] = BackgroundPtr[0];
1846  BackgroundPtr[0] = TempPtr;
1847 
1848  // update headcode graphics depending on Lead entry value
1849  if(LeadElement > -1) // if Lead is -1 then stays as is
1850  {
1852  {
1853  for(int x = 0; x < 4; x++)
1854  {
1855  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1856  }
1857  }
1858  else
1859  {
1860  for(int x = 0; x < 4; x++)
1861  {
1863  }
1864  }
1865  }
1866  if(TrainMode == Timetable)
1867  {
1869  }
1870  else
1871  {
1873  }
1875 
1876  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1877  if(LeadElement > -1)
1878  {
1879  if(Straddle == MidLag)
1880  // just about to move half onto the new lead element
1881  {
1883  // pick up new background bitmap [0]
1885  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1886  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1887  // check if own ID for entry at continuation, else crashes into itself!
1888  {
1889  // OK if crossing on a bridge
1890  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1891  if(OtherTrainEntryPos == -1)
1892  {
1893  throw Exception("Error - OtherTrainEntryPos not set");
1894  }
1895  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1896  // LeadEntryPos for rear end crashes
1897  (LeadExitPos == OtherTrainEntryPos))
1898  // LeadExitPos for head-on crashes
1899  {
1901  Crashed = true; // only set if Straddle = MidLag
1902  CallingOnFlag = false;
1903  // in case was set, need to disable call on if call on button had been pressed
1904  }
1905  }
1906  else if(MidElement > -1) // will be -1 for continuation entries
1907  {
1908  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1909  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1910  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1911  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1912  int OtherTrainID = -1;
1913  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1914  {
1915  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1916  {
1917  TrainCrashedInto = OtherTrainID;
1918  Crashed = true; // only set if Straddle = MidLag
1919  CallingOnFlag = false;
1920  // in case was set, need to disable call on if call on button had been pressed
1921  }
1922  }
1923  }
1924  }
1925  else
1926  {
1928  // pick up new background bitmap [0]
1930  }
1931  PlotElement[0] = LeadElement;
1933  PlotTrainGraphic(12, 0, Display);
1934  }
1935  if(MidElement > -1)
1936  {
1937  PlotElement[2] = MidElement;
1939  PlotTrainGraphic(1, 2, Display);
1940  }
1941  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1942  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1943  if(Straddle == MidLag)
1944  {
1945  if(MidElement > -1)
1946  {
1947  PlotElement[1] = MidElement;
1949  PlotTrainGraphic(2, 1, Display);
1950  }
1951  if(LagElement > -1)
1952  {
1953  PlotElement[3] = LagElement;
1955  PlotTrainGraphic(3, 3, Display);
1956  }
1957  }
1958  else // Straddle == LeadMidLag
1959  {
1960  if(LeadElement > -1)
1961  {
1962  PlotElement[1] = LeadElement;
1964  PlotTrainGraphic(4, 1, Display);
1965  }
1966  if(MidElement > -1)
1967  {
1968  PlotElement[3] = MidElement;
1970  PlotTrainGraphic(5, 3, Display);
1971  }
1972  }
1973  if(Crashed)
1974  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1975  {
1980  // in case was set, need to disable call on if call on button had been pressed
1987  Straddle = LeadMidLag;
1988  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1989  Display->Update();
1990  // resurrected when Update() dropped from PlotOutput etc
1991  Utilities->CallLogPop(660);
1992  return;
1993  }
1994  // deal here with station stops & pass times after all replotting done but before Straddle changed
1995  if(TrainMode == Timetable)
1996  {
1997  if(Straddle == LeadMidLag)
1998  {
1999  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2000  {
2001  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2002  // to point to the location arrival entry - before a change of direction
2003  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2004  bool StopRequired = false;
2005  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2006  if(TTVPos > -1) // -1 if can't find it or if name is ""
2007  {
2008  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2009  // or a station where next element contains a train or a stop signal, if so
2010  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2011  // to test the actual track the train is on since it can't be a platform
2012  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2013  TTrackElement NextTrackElement; // default for now
2014  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2015  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2016  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2017  int NextElementEntryPos = -1;
2018  int NextElementExitPos = -1;
2019  bool TrainOnNextElement = false;
2020  bool StopSignalAtNextElement = false;
2021  if(ForwardConnection)
2022  // if no forward connection can't derive anything from it without errors
2023  {
2024  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2025  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2026  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2027  // this is only for signals so no need to worry about points ambiguity
2028  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2029  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2030  }
2031  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
2032  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2033  {
2034  if(TTVPos > 0)
2035  {
2037  ActionVectorEntryPtr += TTVPos;
2038  }
2039  if(StopRequired)
2040  {
2041  StoppedAtLocation = true;
2042  StoppedAtSignal = false;
2043  // may have been set earlier at line 925 so need to reset as
2044  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2045  // in zoom out mode
2046  if(!TrainFailed)
2047  {
2049  // pale green
2050  }
2053  {
2054  TimeTimeLocArrived = true;
2055  // used in case of later signaller control, when need to know
2056  // whether had arrived or not, to avoid sending the arrival
2057  // message twice, see TInterface::TimetableControl1Click
2058  }
2059  }
2060  else
2061  {
2063  }
2065  {
2067  }
2068  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2070  }
2071  }
2072  }
2073  }
2074  }
2075  if(Straddle == MidLag)
2076  {
2077  Straddle = LeadMidLag;
2078  FirstHalfMove = false;
2079  }
2080  else if(Straddle == LeadMidLag)
2081  {
2082  Straddle = LeadMid;
2083  FirstHalfMove = true;
2084  }
2085  else if(Straddle == LeadMid)
2086  {
2087  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2088  }
2089  if(TrainFailurePending) // ok, moving but PlotElements set above
2090  {
2091  TrainHasFailed(7);
2092  }
2093  Display->Update();
2094  // need to keep this since Update() not called for PlotSmallOutput as too slow
2095  Utilities->CallLogPop(661);
2096 }
2097 
2098 // ----------------------------------------------------------------------------
2099 
2100 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2101 {
2102  switch(CodeChar)
2103  {
2104  case '0':
2105  return(RailGraphics->Code0);
2106 
2107  case '1':
2108  return(RailGraphics->Code1);
2109 
2110  case '2':
2111  return(RailGraphics->Code2);
2112 
2113  case '3':
2114  return(RailGraphics->Code3);
2115 
2116  case '4':
2117  return(RailGraphics->Code4);
2118 
2119  case '5':
2120  return(RailGraphics->Code5);
2121 
2122  case '6':
2123  return(RailGraphics->Code6);
2124 
2125  case '7':
2126  return(RailGraphics->Code7);
2127 
2128  case '8':
2129  return(RailGraphics->Code8);
2130 
2131  case '9':
2132  return(RailGraphics->Code9);
2133 
2134  case 'A':
2135  return(RailGraphics->CodeA);
2136 
2137  case 'B':
2138  return(RailGraphics->CodeB);
2139 
2140  case 'C':
2141  return(RailGraphics->CodeC);
2142 
2143  case 'D':
2144  return(RailGraphics->CodeD);
2145 
2146  case 'E':
2147  return(RailGraphics->CodeE);
2148 
2149  case 'F':
2150  return(RailGraphics->CodeF);
2151 
2152  case 'G':
2153  return(RailGraphics->CodeG);
2154 
2155  case 'H':
2156  return(RailGraphics->CodeH);
2157 
2158  case 'I':
2159  return(RailGraphics->CodeI);
2160 
2161  case 'J':
2162  return(RailGraphics->CodeJ);
2163 
2164  case 'K':
2165  return(RailGraphics->CodeK);
2166 
2167  case 'L':
2168  return(RailGraphics->CodeL);
2169 
2170  case 'M':
2171  return(RailGraphics->CodeM);
2172 
2173  case 'N':
2174  return(RailGraphics->CodeN);
2175 
2176  case 'O':
2177  return(RailGraphics->CodeO);
2178 
2179  case 'P':
2180  return(RailGraphics->CodeP);
2181 
2182  case 'Q':
2183  return(RailGraphics->CodeQ);
2184 
2185  case 'R':
2186  return(RailGraphics->CodeR);
2187 
2188  case 'S':
2189  return(RailGraphics->CodeS);
2190 
2191  case 'T':
2192  return(RailGraphics->CodeT);
2193 
2194  case 'U':
2195  return(RailGraphics->CodeU);
2196 
2197  case 'V':
2198  return(RailGraphics->CodeV);
2199 
2200  case 'W':
2201  return(RailGraphics->CodeW);
2202 
2203  case 'X':
2204  return(RailGraphics->CodeX);
2205 
2206  case 'Y':
2207  return(RailGraphics->CodeY);
2208 
2209  case 'Z':
2210  return(RailGraphics->CodeZ);
2211 
2212  case 'a':
2213  return(RailGraphics->Code_a);
2214 
2215  case 'b':
2216  return(RailGraphics->Code_b);
2217 
2218  case 'c':
2219  return(RailGraphics->Code_c);
2220 
2221  case 'd':
2222  return(RailGraphics->Code_d);
2223 
2224  case 'e':
2225  return(RailGraphics->Code_e);
2226 
2227  case 'f':
2228  return(RailGraphics->Code_f);
2229 
2230  case 'g':
2231  return(RailGraphics->Code_g);
2232 
2233  case 'h':
2234  return(RailGraphics->Code_h);
2235 
2236  case 'i':
2237  return(RailGraphics->Code_i);
2238 
2239  case 'j':
2240  return(RailGraphics->Code_j);
2241 
2242  case 'k':
2243  return(RailGraphics->Code_k);
2244 
2245  case 'l':
2246  return(RailGraphics->Code_l);
2247 
2248  case 'm':
2249  return(RailGraphics->Code_m);
2250 
2251  case 'n':
2252  return(RailGraphics->Code_n);
2253 
2254  case 'o':
2255  return(RailGraphics->Code_o);
2256 
2257  case 'p':
2258  return(RailGraphics->Code_p);
2259 
2260  case 'q':
2261  return(RailGraphics->Code_q);
2262 
2263  case 'r':
2264  return(RailGraphics->Code_r);
2265 
2266  case 's':
2267  return(RailGraphics->Code_s);
2268 
2269  case 't':
2270  return(RailGraphics->Code_t);
2271 
2272  case 'u':
2273  return(RailGraphics->Code_u);
2274 
2275  case 'v':
2276  return(RailGraphics->Code_v);
2277 
2278  case 'w':
2279  return(RailGraphics->Code_w);
2280 
2281  case 'x':
2282  return(RailGraphics->Code_x);
2283 
2284  case 'y':
2285  return(RailGraphics->Code_y);
2286 
2287  case 'z':
2288  return(RailGraphics->Code_z);
2289 
2290  default:
2291  return(RailGraphics->TempHeadCode);
2292  }
2293 }
2294 
2295 // ----------------------------------------------------------------------------
2296 
2297 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2298 {
2299  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2300  if(Code.Length() != 4)
2301  {
2302  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2303  }
2304  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2305  {
2306  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2307  }
2308  if(BackgroundColour != clB5G5R5)
2309  // i.e. not the basic graphic colour as loaded from resource file
2310  {
2311  for(int x = 0; x < 4; x++)
2312  {
2314  }
2315  }
2316  Utilities->CallLogPop(1484);
2317 }
2318 
2319 // ----------------------------------------------------------------------------
2320 
2321 void TTrain::GetLeadElement(int Caller)
2322 // assumes Mid & Lag already set, sets LeadElement,
2323 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2324 {
2325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2326  DerailPending = false;
2330  {
2331  // attr 0=straight, - links 0 & 1 (0 = lead)
2332  // attr 1=diverging, - links 2 & 3 (2 = lead)
2333  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2334  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2335 
2336  // if enter at lead, exit at whatever attr set at
2337  // if enter at lag, exit at lead, but set derail wrt attribute
2338  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2339  {
2340  LeadExitPos = 1;
2341  }
2342 
2343  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2344  // best to be on safe side
2345  else if(LeadEntryPos == 0)
2346  {
2347  LeadEntryPos = 2;
2348  LeadExitPos = 3;
2349  }
2350  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2351  {
2352  LeadEntryPos = 0;
2353  LeadExitPos = 1;
2354  }
2355  else if(LeadEntryPos == 2)
2356  {
2357  LeadExitPos = 3;
2358  }
2359 
2360  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2361  {
2362  LeadExitPos = 0;
2363  }
2364  else if(LeadEntryPos == 1)
2365  {
2366  LeadExitPos = 0;
2367  DerailPending = true;
2368  }
2369  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2370  {
2371  LeadExitPos = 0;
2372  DerailPending = true;
2373  }
2374  else if(LeadEntryPos == 3)
2375  {
2376  LeadExitPos = 0;
2377  }
2378  }
2379  else if(LeadEntryPos == 0)
2380  {
2381  LeadExitPos = 1;
2382  }
2383  else if(LeadEntryPos == 1)
2384  {
2385  LeadExitPos = 0;
2386  }
2387  else if(LeadEntryPos == 2)
2388  {
2389  LeadExitPos = 3;
2390  }
2391  else if(LeadEntryPos == 3)
2392  {
2393  LeadExitPos = 2;
2394  }
2395  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2396 /* signal check moved to Update() function
2397  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2398  && (TrackElement.Attribute == 0))//0 = red
2399  {
2400  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2401  }
2402  else
2403  {
2404  StoppedAtSignal = false;
2405  }
2406 */
2407  Utilities->CallLogPop(662);
2408 }
2409 
2410 // ----------------------------------------------------------------------------
2411 
2412 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2413 {
2414  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2415  switch(Link)
2416  {
2417  case 1:
2418  {
2419  HOffset = 0;
2420  VOffset = 0;
2421  break;
2422  }
2423 
2424  case 2:
2425  {
2426  HOffset = 4;
2427  VOffset = 0;
2428  break;
2429  }
2430 
2431  case 3:
2432  {
2433  HOffset = 8;
2434  VOffset = 0;
2435  break;
2436  }
2437 
2438  case 4:
2439  {
2440  HOffset = 0;
2441  VOffset = 4;
2442  break;
2443  }
2444 
2445  case 6:
2446  {
2447  HOffset = 8;
2448  VOffset = 4;
2449  break;
2450  }
2451 
2452  case 7:
2453  {
2454  HOffset = 0;
2455  VOffset = 8;
2456  break;
2457  }
2458 
2459  case 8:
2460  {
2461  HOffset = 4;
2462  VOffset = 8;
2463  break;
2464  }
2465 
2466  case 9:
2467  {
2468  HOffset = 8;
2469  VOffset = 8;
2470  break;
2471  }
2472 
2473  default:
2474  {
2475  throw Exception("Error in GetOffsetValues - Link value wrong");
2476  }
2477  }
2478  Utilities->CallLogPop(674);
2479 }
2480 
2481 // ---------------------------------------------------------------------------
2482 
2483 bool TTrain::LowEntryValue(int EntryLink) const
2484 {
2485 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2486  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2487 */
2488  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2489  {
2490  return(true);
2491  }
2492  else
2493  {
2494  return(false);
2495  }
2496 }
2497 
2498 // ---------------------------------------------------------------------------
2499 
2500 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2501 {
2502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2503  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2504  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2505  // default values
2506  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2507 
2508  TAllRoutes::TRouteType RouteType;
2509 
2510  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2511 
2512  TRect SourceRect, DestRect;
2513 
2514  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2515  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2516  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2517  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2518 
2519  TempGraphic->PixelFormat = pf8bit;
2520  TempGraphic->Width = 16;
2521  TempGraphic->Height = 16;
2522  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2523 
2524  if(TempElement.TrackType == Points)
2525  {
2526  TempGraphic->Assign(TempElement.GraphicPtr);
2527  TempGraphic->Transparent = true;
2528  TempGraphic->TransparentColor = Utilities->clTransparent;
2529  if(RouteType == TAllRoutes::AutoSigsRoute)
2530  {
2531  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2532  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2533  }
2534  else
2535  {
2536  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2537  }
2538  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2539  }
2540  else if(TempElement.TrackType == GapJump) // plot set gap
2541  {
2542  if(TempElement.SpeedTag == 88)
2543  {
2544  TempGraphic->Assign(RailGraphics->gl88set);
2545  }
2546  else if(TempElement.SpeedTag == 89)
2547  {
2548  TempGraphic->Assign(RailGraphics->gl89set);
2549  }
2550  else if(TempElement.SpeedTag == 90)
2551  {
2552  TempGraphic->Assign(RailGraphics->gl90set);
2553  }
2554  else if(TempElement.SpeedTag == 91)
2555  {
2556  TempGraphic->Assign(RailGraphics->gl91set);
2557  }
2558  else if(TempElement.SpeedTag == 92)
2559  {
2560  TempGraphic->Assign(RailGraphics->gl92set);
2561  }
2562  else if(TempElement.SpeedTag == 93)
2563  {
2564  TempGraphic->Assign(RailGraphics->bm93set);
2565  }
2566  else if(TempElement.SpeedTag == 94)
2567  {
2568  TempGraphic->Assign(RailGraphics->bm94set);
2569  }
2570  else if(TempElement.SpeedTag == 95)
2571  {
2572  TempGraphic->Assign(RailGraphics->gl95set);
2573  }
2574  TempGraphic->Transparent = true;
2575  TempGraphic->TransparentColor = Utilities->clTransparent;
2576  if(RouteType == TAllRoutes::AutoSigsRoute)
2577  {
2578  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2579  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2580  }
2581  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2582  }
2583  // new for version 0.6
2584  else if(TempElement.TrackType == SignalPost)
2585  {
2586  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2587  {
2588  for(int x = 0; x < 40; x++)
2589  {
2590  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2591  // need to stop aspect
2592  {
2593  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2594  break;
2595  }
2596  }
2597  }
2598  else // normal signal
2599  {
2600  TempGraphic->Assign(TempElement.GraphicPtr);
2601  // GraphicPtr set to normal signal in a signal track element
2602  }
2603  TempGraphic->Transparent = true;
2604  TempGraphic->TransparentColor = Utilities->clTransparent;
2605  if(RouteType == TAllRoutes::AutoSigsRoute)
2606  {
2607  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2608  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2609  }
2610  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2611  }
2612  else
2613  {
2614  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2615  // can't name points gaps or signals so 'else' OK
2616  bool FoundFlag;
2617  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2618  if(FoundFlag)
2619  {
2621  {
2622  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2623  TempGraphic->Assign(RailGraphics->bmName);
2624  TempGraphic->Transparent = true;
2625  TempGraphic->TransparentColor = Utilities->clTransparent;
2626  if(RouteType == TAllRoutes::AutoSigsRoute)
2627  {
2628  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2629  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2630  }
2631  else
2632  {
2633  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2634  }
2635  // draw track on top
2636  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2637  }
2638  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2639  {
2640  TempGraphic->Assign(TempElement.GraphicPtr);
2641  TempGraphic->Transparent = true;
2642  TempGraphic->TransparentColor = Utilities->clTransparent;
2643  // note that can't be an AutoSigsRoute
2644  // now overlay the LC central portion
2645  int BDVectorPos = -1; //not used
2646  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2647  {
2648  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2649  }
2650  else
2651  {
2652  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2653  }
2654  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2655  }
2656  else
2657  {
2658  TempGraphic->Assign(TempElement.GraphicPtr);
2659  TempGraphic->Transparent = true;
2660  TempGraphic->TransparentColor = Utilities->clTransparent;
2661  if(RouteType == TAllRoutes::AutoSigsRoute)
2662  {
2663  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2664  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2665  }
2666  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2667  }
2668  }
2669  else
2670  {
2671  TempGraphic->Assign(TempElement.GraphicPtr);
2672  TempGraphic->Transparent = true;
2673  TempGraphic->TransparentColor = Utilities->clTransparent;
2674  if(RouteType == TAllRoutes::AutoSigsRoute)
2675  {
2676  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2677  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2678  }
2679  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2680  }
2681  }
2682  delete TempGraphic;
2683  Utilities->CallLogPop(675);
2684 }
2685 
2686 // ---------------------------------------------------------------------------
2687 
2688 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2689 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2690 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2691 /*
2692 
2693  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2694  {
2695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2696  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2697  TAllRoutes::TRouteType RouteType;
2698  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2699  // default values
2700  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2701  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2702  TRect SourceRect, DestRect, ScreenSourceRect;
2703  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2704 
2705  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2706  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2707  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2708 
2709  //add text & user graphics if any to *GraphicPtr prior to adding the track
2710  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2711  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2712  int Right = Left + 8;
2713  int Bottom = Top + 8;
2714  ScreenSourceRect.init(Left, Top, Right, Bottom);
2715  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2716  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2717 
2718  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2719  TempGraphic->PixelFormat = pf8bit;
2720  TempGraphic->Width = 16;
2721  TempGraphic->Height = 16;
2722 
2723  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2724  SourceGraphic->PixelFormat = pf8bit;
2725  SourceGraphic->Width = 16;
2726  SourceGraphic->Height = 16;
2727  SourceGraphic->Transparent = true;
2728  SourceGraphic->TransparentColor = Utilities->clTransparent;
2729 
2730  if (TempElement.TrackType == Points)
2731  {
2732  TempGraphic->Assign(TempElement.GraphicPtr);
2733  TempGraphic->Transparent = true;
2734  TempGraphic->TransparentColor = Utilities->clTransparent;
2735  if (RouteType == TAllRoutes::AutoSigsRoute)
2736  {
2737  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2738  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2739  }
2740  else
2741  {
2742  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2743  }
2744  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2745  }
2746  else if (TempElement.TrackType == GapJump) // plot set gap
2747  {
2748  if (TempElement.SpeedTag == 88)
2749  TempGraphic->Assign(RailGraphics->gl88set);
2750  else if (TempElement.SpeedTag == 89)
2751  TempGraphic->Assign(RailGraphics->gl89set);
2752  else if (TempElement.SpeedTag == 90)
2753  TempGraphic->Assign(RailGraphics->gl90set);
2754  else if (TempElement.SpeedTag == 91)
2755  TempGraphic->Assign(RailGraphics->gl91set);
2756  else if (TempElement.SpeedTag == 92)
2757  TempGraphic->Assign(RailGraphics->gl92set);
2758  else if (TempElement.SpeedTag == 93)
2759  TempGraphic->Assign(RailGraphics->bm93set);
2760  else if (TempElement.SpeedTag == 94)
2761  TempGraphic->Assign(RailGraphics->bm94set);
2762  else if (TempElement.SpeedTag == 95)
2763  TempGraphic->Assign(RailGraphics->gl95set);
2764  TempGraphic->Transparent = true;
2765  TempGraphic->TransparentColor = Utilities->clTransparent;
2766  if (RouteType == TAllRoutes::AutoSigsRoute) {
2767  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2768  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2769  }
2770  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2771  }
2772  // new for version 0.6
2773  else if (TempElement.TrackType == SignalPost)
2774  {
2775  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2776  {
2777  for (int x = 0; x < 40; x++)
2778  {
2779  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2780  // need to stop aspect
2781  {
2782  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2783  break;
2784  }
2785  }
2786  }
2787  else // normal signal
2788  {
2789  TempGraphic->Assign(TempElement.GraphicPtr);
2790  // GraphicPtr set to normal signal in a signal track element
2791  }
2792  TempGraphic->Transparent = true;
2793  TempGraphic->TransparentColor = Utilities->clTransparent;
2794  if (RouteType == TAllRoutes::AutoSigsRoute) {
2795  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2796  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2797  }
2798  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2799  }
2800  else {
2801  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2802  // can't name points gaps or signals so 'else' OK
2803  bool FoundFlag;
2804  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2805  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2806  if (FoundFlag)
2807  {
2808  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2809  {
2810  GraphicPtr->Canvas->CopyRect(DestRect,
2811  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2812  TempGraphic->Assign(RailGraphics->bmName);
2813  TempGraphic->Transparent = true;
2814  TempGraphic->TransparentColor = Utilities->clTransparent;
2815  if (RouteType == TAllRoutes::AutoSigsRoute)
2816  {
2817  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2818  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2819  }
2820  else
2821  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2822  // draw track on top
2823  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2824  SourceRect);
2825  }
2826  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2827  TempGraphic->Assign(TempElement.GraphicPtr);
2828  TempGraphic->Transparent = true;
2829  TempGraphic->TransparentColor = Utilities->clTransparent;
2830  // note that can't be an AutoSigsRoute
2831  // now overlay the LC central portion
2832  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2833  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2834  SourceRect);
2835  }
2836  else {
2837  TempGraphic->Assign(TempElement.GraphicPtr);
2838  TempGraphic->Transparent = true;
2839  TempGraphic->TransparentColor = Utilities->clTransparent;
2840  if (RouteType == TAllRoutes::AutoSigsRoute) {
2841  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2842  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2843  }
2844  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2845  SourceRect);
2846  }
2847  }
2848  else {
2849  TempGraphic->Assign(TempElement.GraphicPtr);
2850  TempGraphic->Transparent = true;
2851  TempGraphic->TransparentColor = Utilities->clTransparent;
2852  if (RouteType == TAllRoutes::AutoSigsRoute) {
2853  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2854  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2855  }
2856  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2857  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2858  }
2859  }
2860  delete TempGraphic;
2861  delete SourceGraphic;
2862  Utilities->CallLogPop();
2863  }
2864 */
2865 // ---------------------------------------------------------------------------
2866 
2867 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2868 {
2869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2870  if(PlotElement[ArrayNumber] == -1)
2871  {
2872  Utilities->CallLogPop(676);
2873  return; // not plotted yet
2874  }
2875  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2876  // set before plot so gap flashing stops first
2877  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2878  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2879  // Only need to set ID for leading element, stays set until train finally leaves the element
2880  Plotted = true;
2881  Utilities->CallLogPop(677);
2882 }
2883 
2884 // ---------------------------------------------------------------------------
2885 
2886 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2887 {
2888  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2889  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2890 }
2891 
2892 // ---------------------------------------------------------------------------
2893 
2894 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2895 {
2896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2897  HeadCode);
2898  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2899  {
2900  Utilities->CallLogPop(678);
2901  return(true);
2902  }
2903  else
2904  {
2905  Utilities->CallLogPop(679);
2906  return(false);
2907  }
2908 }
2909 
2910 // ---------------------------------------------------------------------------
2911 
2912 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2913 {
2914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2915  "," + HeadCode);
2916  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2917  {
2918  Utilities->CallLogPop(680);
2919  return(true);
2920  }
2921  else
2922  {
2923  Utilities->CallLogPop(681);
2924  return(false);
2925  }
2926 }
2927 
2928 // ---------------------------------------------------------------------------
2929 
2930 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2931 // test whether this train on a bridge on trackpos 0 & 1
2932 {
2933  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2934  HeadCode);
2935  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2936  {
2937  Utilities->CallLogPop(682);
2938  return(false);
2939  }
2940  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2941  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2942  {
2943  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2944  {
2945  throw Exception("Error, same train on two different bridge tracks");
2946  }
2947  else
2948  {
2949  Utilities->CallLogPop(684);
2950  return(true);
2951  }
2952  }
2953  else
2954  {
2955  Utilities->CallLogPop(685);
2956  return(false);
2957  }
2958 }
2959 
2960 // ---------------------------------------------------------------------------
2961 
2962 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2963 // test whether this train on a bridge on trackpos 2 & 3
2964 {
2965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2966  HeadCode);
2967  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2968  {
2969  Utilities->CallLogPop(686);
2970  return(false);
2971  }
2972  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2973  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2974  {
2975  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2976  Utilities->CallLogPop(687);
2977  return(true);
2978  }
2979  else
2980  {
2981  Utilities->CallLogPop(688);
2982  return(false);
2983  }
2984 }
2985 
2986 // ---------------------------------------------------------------------------
2987 
2988 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2989 {
2990  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2991  AnsiString(EntryPos) + "," + HeadCode);
2992  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2993 
2994  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2995  if(Track->GapFlashFlag)
2996  {
2998  {
3001  Track->GapFlashFlag = false;
3002  }
3003  }
3004  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3005  {
3006  if(EntryPos == -1)
3007  {
3008  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3009  }
3010  if(EntryPos < 2)
3011  {
3012  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3013  }
3014  else
3015  {
3016  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3017  }
3018  }
3019  Utilities->CallLogPop(690);
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3025 {
3026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3027  AnsiString(EntryPos) + "," + HeadCode);
3028  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3029  {
3030  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3031  }
3032  else
3033  {
3034  if(EntryPos == -1)
3035  {
3036  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3037  }
3038  if(EntryPos < 2)
3039  {
3040  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3041  }
3042  else
3043  {
3044  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3045  }
3046  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3047  // i.e. other train on track 2&3
3048  {
3049  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3050  }
3051  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3052  // i.e. other train on track 1&2
3053  {
3054  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3055  }
3056  else
3057  {
3058  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3059  }
3060  }
3061  Utilities->CallLogPop(691);
3062 }
3063 
3064 // ---------------------------------------------------------------------------
3065 
3066 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3067 {
3068  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3069  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3070  int LockedVectorNumber;
3071 
3072  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3073  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3074  {
3075  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3076  Utilities->CallLogPop(692);
3077  return;
3078  }
3079  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3080  // i.e other track is in a marked route
3081  // LinkPos doesn't have to be the entry position for the above check
3082  {
3083  TRect SourceRect, DestRect;
3084  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3085  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3086  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3087  // identify the route element for the other track
3088  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3089  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3090  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3091  int FirstELink, SecondELink = -1;
3092  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3093  // must be at least one
3094  if(RoutePair2.first > -1)
3095  {
3096  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3097  }
3098  TPrefDirElement RouteElement;
3099  // Graphics::TBitmap *RouteGraphic;
3100  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3101  // i.e. other track is in RoutePair2
3102  {
3103  if(SecondELink == -1)
3104  {
3105  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3106  }
3107  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3108  // error if both have same Link number
3109  {
3110  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3111  }
3112  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3113  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3114  }
3115  else // other track is in RoutePair1
3116  {
3117  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3118  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3119  }
3120  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3121  DestGraphic->PixelFormat = pf8bit;
3122  DestGraphic->Width = 8;
3123  DestGraphic->Height = 8;
3124  DestGraphic->Transparent = true;
3125  // has to be transparent or will overwrite the track that the train has just left
3126  DestGraphic->TransparentColor = Utilities->clTransparent;
3127  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3128  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3129  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3130  // plot locked route marker for other route if appropriate
3131  TPrefDirElement PrefDirElement; // holder for next call, unused
3132  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3133  if(StraddleValue == LeadMidLag)
3134  {
3136  PrefDirElement, LockedVectorNumber))
3137  {
3138  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3139  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3140  }
3141  }
3142  delete DestGraphic;
3143  }
3144  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3145  // also can only be a bridge or trains either have already or soon will crash
3146  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3147  {
3148  Utilities->CallLogPop(695);
3149  return;
3150  }
3151  if(ElementEntryPos > 1) // other train is on track 01
3152  {
3153  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3154  {
3156  }
3157  }
3158  else // other train is on track 23
3159  {
3160  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3161  {
3163  }
3164  }
3165  Utilities->CallLogPop(696);
3166 }
3167 
3168 // ---------------------------------------------------------------------------
3169 
3170 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3171 {
3172  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3173  AnsiString(EntryPos) + "," + HeadCode);
3174  int RouteNumber;
3175  bool WrongRoute = false;
3176  TPrefDirElement RouteElement;
3178  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3179 
3180  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3181  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3182  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3183  {
3184  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3185  {
3186  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3187  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3188  {
3189  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3190  {
3191  // don't call for stub end routes
3193  }
3194  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3195  Utilities->CallLogPop(697);
3196  return;
3197  }
3198  }
3199  // also need to check for a route on a crossing diagonal
3200  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3201  int LinkNumber = TrackElement.Link[EntryPos];
3202  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3203  {
3204  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3205  {
3206  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3207  bool LogActionErrorCalled = false;
3208  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3209  if(LinkNumber == 1)
3210  {
3211  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3212  {
3213  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3214  {
3215  // don't call for stub end routes
3217  LogActionErrorCalled = true;
3218  }
3219  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3220  }
3221  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3222  // not else in case have different routes on each diagonal, though shouldn't be possible
3223  {
3224  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3225  {
3226  // don't call for stub end routes
3228  }
3229  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3230  }
3231  }
3232 
3233  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3234  else if(LinkNumber == 3)
3235  {
3236  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3237  {
3238  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3239  {
3240  // don't call for stub end routes
3242  LogActionErrorCalled = true;
3243  }
3244  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3245  }
3246  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3247  // not else in case have different routes on each diagonal, though shouldn't be possible
3248  {
3249  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3250  {
3251  // don't call for stub end routes
3253  }
3254  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3255  }
3256  }
3257 
3258  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3259  else if(LinkNumber == 7)
3260  {
3261  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3262  {
3263  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3264  {
3265  // don't call for stub end routes
3267  LogActionErrorCalled = true;
3268  }
3269  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3270  }
3271  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3272  // not else in case have different routes on each diagonal, though shouldn't be possible
3273  {
3274  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3275  {
3276  // don't call for stub end routes
3278  }
3279  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3280  }
3281  }
3282 
3283  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3284  else if(LinkNumber == 9)
3285  {
3286  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3287  {
3288  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3289  {
3290  // don't call for stub end routes
3292  LogActionErrorCalled = true;
3293  }
3294  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3295  }
3296  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3297  // not else in case have different routes on each diagonal, though shouldn't be possible
3298  {
3299  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3300  {
3301  // don't call for stub end routes
3303  }
3304  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3305  }
3306  }
3307  }
3308  }
3309  Utilities->CallLogPop(698);
3310  return; // no route on other track or no other track
3311  }
3312  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3313  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3314  {
3315  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3316  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3317  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3318  {
3319  if(RouteElement.GetELinkPos() == EntryPos)
3320  {
3321  Utilities->CallLogPop(699);
3322  return; // right direction
3323  }
3324  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3325  {
3326  Utilities->CallLogPop(700);
3327  return; // right direction (points)
3328  }
3329  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3330  {
3331  Utilities->CallLogPop(701);
3332  return; // right direction (points)
3333  }
3334  else if(RouteElement.GetXLinkPos() == EntryPos)
3335  {
3336  WrongRoute = true;
3337  break; // wrong direction
3338  }
3339  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3340  {
3341  WrongRoute = true;
3342  break; // wrong direction
3343  }
3344  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3345  {
3346  WrongRoute = true;
3347  break; // wrong direction
3348  }
3349  }
3350  }
3351  if(!WrongRoute)
3352  {
3353  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3354  }
3355  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3356  {
3357  // don't call for stub end routes
3359  }
3360  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3361  Utilities->CallLogPop(703);
3362 }
3363 
3364 // ---------------------------------------------------------------------------
3365 
3366 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3367 {
3368  if(BackgroundColour == NewBackgroundColour)
3369  {
3370  return; // don't replot if already correct
3371 
3372  }
3373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3374  bool ColourError = false, ColourError2 = false;
3375 
3376  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3377  if(ColourError)
3378  {
3379  ColourError2 = true;
3380  }
3381  for(int x = 0; x < 4; x++)
3382  {
3383  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3384  if(ColourError)
3385  {
3386  ColourError2 = true;
3387  }
3388  }
3389  if(ColourError2)
3390  {
3392  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3393  }
3394  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3395  // of motion
3396  for(int x = 0; x < 4; x++)
3397  {
3398  PlotTrainGraphic(6, x, Disp);
3399  }
3400  BackgroundColour = NewBackgroundColour;
3401  Display->Update();
3402  // need to keep this since Update() not called for PlotSmallOutput as too slow
3403  Utilities->CallLogPop(704);
3404 }
3405 
3406 // ---------------------------------------------------------------------------
3407 
3408 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3409 /*
3410 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3411 
3412 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3413 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3414 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3415 full-element moves.
3416 
3417 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3418 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3419 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3420 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3421 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3422 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3423 In this case set the brake rate to maximum to stop as soon as possible.
3424 
3425 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3426 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3427 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3428 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3429 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3430 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3431 
3432 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3433 first to see whether buffers or continuation) in turn is examined: first the length of the
3434 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3435 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3436 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3437 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3438 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3439 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3440 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3441 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3442 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3443 siding then again emeregency braking may be necessary and a crash may result.
3444 
3445 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3446 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3447 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3448 buffer, then the train accelerates for half the element and brakes for the other half.
3449 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3450 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3451 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3452 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3453 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3454 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3455 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3456 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3457 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3458 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3459 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3460 
3461 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3462 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3463 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3464 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3465 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3466 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3467 MaxBrakeRate.
3468 
3469 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3470 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3471 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3472 
3473 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3474 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3475 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3476 
3477 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3478 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3479 when Straddle == LeadMidLag
3480 */
3481 {
3482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3483  AnsiString(EntryPos) + "," + HeadCode);
3484  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3485  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3486  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3487  TrainInFrontInSignallerModeFlag = false;
3488  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3489  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3490  bool SignallerStopRequired = false;
3491 
3493  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3494 
3495  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3496 
3497  OneLengthAccelDecel = false;
3498  BrakeRate = 0;
3499 
3500 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3501  if(CurrentTrackVectorPosition > -1)
3502  {
3503  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3504  {
3505  if((EntryPos == 0) || (EntryPos == 2))
3506  {
3507  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3508  {
3509  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3510  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3511  }
3512  else
3513  {
3514  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3515  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3516  }
3517  }
3518  else if(EntryPos == 1)
3519  {
3520  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3521  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3522  }
3523  else // == 3
3524  {
3525  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3526  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3527  }
3528  }
3529  else
3530  {
3531  if(EntryPos > 1)
3532  {
3533  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3534  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3535  }
3536  else
3537  {
3538  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3539  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3540  }
3541  }
3542  EntryHalfLength = CurrentElementHalfLength;
3543  FrontElementLength = 2 * CurrentElementHalfLength;
3544  }
3545  else
3546  {
3547  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3548  }
3549  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3550  {
3551  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3552  }
3553  // check if zero entry speed with another train directly in front & if so remain stopped
3554  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3555  {
3556  EntrySpeed = 0;
3557  ExitSpeedHalf = 0;
3558  ExitSpeedFull = 0;
3559  MaxExitSpeed = 0;
3560  BrakeRate = 0;
3561  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3562  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3563  StoppedForTrainInFront = true;
3564  Utilities->CallLogPop(705);
3565  return;
3566  }
3567  // new at v2.4.0 - check for stopped and zero power
3568  if((EntrySpeed < 1) && PowerAtRail < 1)
3569  {
3570  EntrySpeed = 0;
3571  ExitSpeedHalf = 0;
3572  ExitSpeedFull = 0;
3573  MaxExitSpeed = 0;
3574  BrakeRate = 0;
3575  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3576  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3577  StoppedWithoutPower = true;
3578  Utilities->CallLogPop(2125);
3579  return;
3580  }
3581 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3582  if(BeingCalledOn)
3583  {
3584  LimitingSpeed = CallOnMaxSpeed;
3585  }
3586  else
3587  {
3588  LimitingSpeed = MaximumSpeedLimit;
3589  }
3590  if(LimitingSpeed > FrontElementSpeedLimit)
3591  {
3592  LimitingSpeed = FrontElementSpeedLimit;
3593  }
3594  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3595  {
3596  LimitingSpeed = MaxRunningSpeed;
3597  }
3598  FrontElementMaxSpeed = LimitingSpeed;
3599 
3600 /*
3601  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3602  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3603  (2) V/3.6 = U/3.6 - FT;
3604  (3) S = UT/3.6 - 0.5FT^2
3605  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3606 
3607  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3608  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3609  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3610  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3611  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3612 
3613  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3614  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3615  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3616 */
3617 
3618 // check if running past a red signal without permission
3619  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3620  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3621  {
3622  SPADFlag = true; // user has to intervene to reset & restart after spad
3623  }
3624  if(!SPADFlag)
3625  {
3626  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3627 
3628  double ExitSpeedAtMaxBraking;
3629  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3630  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3632  {
3633  ExitSpeedAtMaxBraking = 0;
3634  }
3635  else
3636  {
3637  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3638  }
3639  double SpeedToUse;
3640  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3641  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3642  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3643  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3644  {
3645  SpeedToUse = ExitSpeedAtMaxBraking;
3646  }
3647  else
3648  {
3649  SpeedToUse = LimitingSpeed;
3650  }
3651  if(ExitSpeedFull > SpeedToUse)
3652  {
3653  ExitSpeedFull = SpeedToUse;
3654  }
3655  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3656  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3657 
3658  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3659  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3660  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3661 
3662  do
3663  {
3664  RedSignalFlag = false;
3665  BuffersFlag = false;
3666  StationFlag = false;
3667  BuffersOrContinuationNowFlag = false;
3668  ContinuationNextFlag = false;
3669  // have to reset this after the above test
3670  // add current element length to CumulativeLength
3671  CumulativeLength += (2 * CurrentElementHalfLength);
3672  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3673  {
3674  SignallerStopRequired = true;
3675  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3676  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3677  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3678  if(SignallerStopBrakeRate < TempBR)
3679  {
3680  SignallerStopBrakeRate = TempBR;
3681  }
3682  }
3683  // first check for stops within the length of the current element, where don't want any more checks & don't want
3684  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3685  // during the last loop when the NextTrackVectorPosition was the signal.
3686 
3687  // check if current element is a buffer
3688  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3689  {
3690  // no need to add in the length of this element to CumulativeLength as already included
3691  BuffersFlag = true;
3692  }
3693  // check if current element is a station stop
3694  if(TrainMode == Timetable)
3695  {
3696  bool StopRequired = false;
3697  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3698  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3699  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3700  {
3701  // no need to add in the length of element to CumulativeLength
3702  if(StopRequired)
3703  {
3704  StationFlag = true;
3705  }
3706  }
3707  }
3708  else
3709  {
3710  StationFlag = false;
3711  }
3712  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3713  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3714  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3715  {
3716  BuffersOrContinuationNowFlag = true;
3717  }
3718  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3719  {
3720  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3721  {
3722  if((EntryPos == 0) || (EntryPos == 2))
3723  {
3724  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3725  {
3726  ExitPos = 1;
3727  }
3728  else
3729  {
3730  ExitPos = 3;
3731  }
3732  }
3733  else
3734  {
3735  ExitPos = 0;
3736  }
3737  }
3738  else
3739  {
3740  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3741  }
3742  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3743  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3744  if(NextTrackVectorPosition > -1)
3745  {
3746  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3747  // this test & section added at v0.6
3748  {
3749  if((NextEntryPos == 0) || (NextEntryPos == 2))
3750  {
3751  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3752  {
3753  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3754  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3755  }
3756  else
3757  {
3758  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3759  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3760  }
3761  }
3762  else if(NextEntryPos == 1)
3763  {
3764  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3765  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3766  }
3767  else // == 3
3768  {
3769  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3770  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3771  }
3772  }
3773  else
3774  {
3775  if(NextEntryPos > 1)
3776  {
3777  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3778  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3779  }
3780  else
3781  {
3782  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3783  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3784  }
3785  }
3786  }
3787  else
3788  {
3789  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3790  }
3791  // now check for stops, first cover those where don't want to add in length of next element
3792  // check if next element is a red signal - Attr 0,
3793  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3794  // CurrentTrackVectorPosition not NextTrackVectorPosition
3795  bool StopRequired;
3796  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3797  {
3798  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3799  {
3800  // no need to add in the length of element to CumulativeLength
3801  RedSignalFlag = true;
3802  }
3803  // next element is a red signal
3804  }
3805  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3806  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3807  // at least one platform element free
3809  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3810  NextEntryPos, TrainID))
3811  {
3812  // no need to add in the length of element to CumulativeLength
3813  if(StopRequired)
3814  {
3815  StationFlag = true;
3816  }
3817  }
3818  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3819  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3820  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3821  {
3822  // no need to add in the length of element to CumulativeLength
3823  TrainInFrontInSignallerModeFlag = true;
3824  }
3825  // check if next element is a buffer
3826  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3827  {
3828  // need to add in the length of that element to CumulativeLength
3829  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3830  BuffersFlag = true;
3831  }
3832  // check if next element is a station stop
3834  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3835  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3836  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3837  {
3838  // need to add in the length of that element to CumulativeLength if a stop required
3839  if(StopRequired)
3840  {
3841  StationFlag = true;
3842  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3843  }
3844  }
3845  }
3846  //now can decide whether need to stop over CumulativeLength
3847  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3848  {
3849  // have to come to a stop over CumulativeLength
3850  if(CumulativeLength == FrontElementLength)
3851  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3852  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3853  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3854  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3855  // if not calc speed at halfway point & if less than above set half speed to this value;
3856  // use constant acceleration in calculating half time point
3857  {
3858  MaxExitSpeed = 0;
3859  double MaxHalfSpeed;
3860  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3861  // have to halve the element length, & can't be zero or negative so no need to test
3862  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3863  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3864  {
3865  MaxHalfSpeed = FrontElementMaxSpeed;
3866  }
3867  else
3868  {
3869  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3870  }
3871  if(MaxHalfSpeed > (2 * EntrySpeed))
3872  // use 2x to prevent kangarooing at last element when had
3873  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3874  {
3875  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3876  0.333334);
3877  bool HalfSpeedLimited = false;
3878  if(MaxHalfSpeed < ExitSpeedHalf)
3879  {
3880  ExitSpeedHalf = MaxHalfSpeed;
3881  HalfSpeedLimited = true;
3882  }
3883  if(PowerAtRail > 1)
3884  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3885  {
3886  // [km/h/3.6 = m/s]
3887  ExitTimeHalf =
3888  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3889  }
3890  else
3891  {
3892  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3893  }
3894  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3895  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3896  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3897  // by a long braking period
3898  ExitSpeedFull = 0;
3899  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3900  if(TempBrakeRate > MaxBrakeRate)
3901  {
3902  TempBrakeRate = MaxBrakeRate;
3903  }
3904  // shouldn't be but leave in anyway
3905  if(TempBrakeRate > BrakeRate)
3906  {
3907  BrakeRate = TempBrakeRate;
3908  }
3909  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3910  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3911  if(HalfSpeedLimited)
3912  // this is the change referred to above
3913  {
3914  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3915  ExitTimeHalf = EntryTime + BrakingTime;
3916  ExitTimeFull = ExitTimeHalf + BrakingTime;
3917  }
3918  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3919  Utilities->CallLogPop(1095);
3920  return;
3921  }
3922  }
3923  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3924  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3925  // calc th, tf, sh, & sf
3926  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3927  if(TempBrakeRate > MaxBrakeRate)
3928  {
3929  TempBrakeRate = MaxBrakeRate;
3930  }
3931  if(TempBrakeRate > BrakeRate)
3932  {
3933  BrakeRate = TempBrakeRate;
3934  }
3935  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3936  if(SignallerStopRequired)
3937  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3938  {
3940  {
3942  // this prevents the brakerate from reducing for a signaller stop
3943  // regardless of other conditions that may change as progress round the loop
3944  }
3945  }
3947  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3948  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3949  {
3951  }
3952  int TempMaxExitSpeed;
3953  // calc current value & if less than MaxExitSpeed set that to this
3954  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3955  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3956  {
3957  MaxExitSpeedAtHalfBraking = 0;
3958  }
3959  else
3960  {
3961  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3962  }
3963  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3964  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3965  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3966  {
3967  TempMaxExitSpeed = FrontElementMaxSpeed;
3968  }
3969  else
3970  {
3971  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3972  }
3973  if(TempMaxExitSpeed < MaxExitSpeed)
3974  {
3975  MaxExitSpeed = TempMaxExitSpeed;
3976  }
3977  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3978  // Cumulativelength, and Cumulativelength
3979 
3980  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3981  {
3982  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3983  if(ExitSpeedHalfSquared < 10)
3984  {
3985  ExitSpeedHalf = 0;
3986  }
3987  else
3988  {
3989  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3990  }
3991  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3992  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3993  if(ExitSpeedFullSquared < 10)
3994  {
3995  ExitSpeedFull = 0;
3996  }
3997  else
3998  {
3999  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4000  }
4001  if((StationFlag) && (CumulativeLength == FrontElementLength))
4002  {
4003  ExitSpeedFull = 0;
4004  // force a stop for station (not for buffers or red signal)
4005  }
4006  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4007  }
4008  // new condition at v2.4.0
4009  else if(PowerAtRail <= 1)
4010  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4011  // avoid using AValue in denominator or have excessively long times
4012  {
4013  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4014  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4015  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4016 
4017  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4018  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4019  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4020  }
4021  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4022  // without the power need above condition or have hours of delay times, above added at v2.4.0
4023  {
4024  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4025  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4026  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4027  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4028  BrakeRate = 0;
4029  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4030  0.333334);
4031  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4032  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4034  // can accelerate continually over the half length
4035  {
4036  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4037  / 86400.0);
4039  // can accelerate continually over the full length
4040  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4041  {
4042  ExitTimeFull =
4043  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4044  }
4045  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4046  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4047  {
4048  // added at v0.6 as a safeguard
4049  if(MaxExitSpeed < EntrySpeed)
4050  {
4052  }
4053  // to prevent DeltaExitTimeToMaxInSecs being negative
4054  if(MaxExitSpeed < 1)
4055  {
4056  MaxExitSpeed = 1;
4057  }
4058  // to prevent divide by zero error
4059  // below as was
4061  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4062  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4063  (1.5 * AValue * AValue);
4064  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4065  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4066  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4067  }
4068  }
4069  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4070  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4071  // second halves of the element
4072  {
4073  // added at v0.6 as a safeguard
4074  if(MaxExitSpeed < EntrySpeed)
4075  {
4077  }
4078  // to prevent DeltaExitTimeToMaxInSecs being negative
4079  if(MaxExitSpeed < 1)
4080  {
4081  MaxExitSpeed = 1; // to prevent divide by zero error
4082  }
4083  // below as was
4085  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4086  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4087  (1.5 * AValue * AValue);
4088  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4089  // remaining distance to half length
4090  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4091  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4093  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4094  }
4095  }
4096  Utilities->CallLogPop(706);
4097  return;
4098  }
4099  else
4100  {
4101  if(!BuffersOrContinuationNowFlag)
4102  {
4103  if(NextSpeedLimit < LimitingSpeed)
4104  {
4105  LimitingSpeed = NextSpeedLimit;
4106  }
4107  }
4108  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4109  int TempMaxExitSpeed;
4110  // calc current value & if less than MaxExitSpeed set that to this
4111  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4112  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4113  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4114  {
4115  MaxExitSpeedAtHalfBraking = 0;
4116  }
4117  else
4118  {
4119  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4120  }
4121  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4122  {
4123  TempMaxExitSpeed = FrontElementMaxSpeed;
4124  }
4125  else
4126  {
4127  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4128  }
4129  if(TempMaxExitSpeed < MaxExitSpeed)
4130  {
4131  MaxExitSpeed = TempMaxExitSpeed;
4132  }
4133  // MaxExitSpeed is an external variable & this can reduce its value
4134  if(EntrySpeed > LimitingSpeed)
4135  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4136  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4137  {
4138  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4139  if(TempBrakeRate > MaxBrakeRate)
4140  {
4141  TempBrakeRate = MaxBrakeRate;
4142  }
4143  // shouldn't be for speedlimits since all known in advance, but include anyway
4144  if(TempBrakeRate > BrakeRate)
4145  {
4146  BrakeRate = TempBrakeRate;
4147  }
4148  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4149  }
4150  }
4151  if(!BuffersOrContinuationNowFlag)
4152  {
4153  CurrentTrackVectorPosition = NextTrackVectorPosition;
4154  EntryPos = NextEntryPos;
4155  CurrentElementHalfLength = NextElementHalfLength;
4156  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4157  {
4158  ContinuationNextFlag = true;
4159  }
4160  }
4161  }
4162  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4164  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4165  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4166  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4167  // stopping distance after it.
4168 
4169  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4170  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4171 
4172  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4173  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4174  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4175  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4176  // too late
4177 
4178  // set final braking or acc'n speed & time values
4179  if(BrakeRate > 0.01)
4180  {
4181  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4182  if(ExitSpeedHalfSquared < 10)
4183  {
4184  ExitSpeedHalf = 0;
4185  }
4186  else
4187  {
4188  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4189  }
4190  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4191  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4192  if(ExitSpeedFullSquared < 10)
4193  {
4194  ExitSpeedFull = 0;
4195  }
4196  else
4197  {
4198  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4199  }
4200  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4201  }
4202  else
4203  {
4204  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4205  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4206  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4207  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4208 
4209  BrakeRate = 0;
4210  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4211  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4212  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4214  {
4215  if(PowerAtRail > 1)
4216  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4217  {
4218  // [km/h/3.6 = m/s]
4219  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4220  / 86400.0);
4221  }
4222  else
4223  {
4224  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4225  }
4227  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4228  {
4229  if(PowerAtRail > 1)
4230  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4231  {
4232  // [km/h/3.6 = m/s]
4233  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4234  / 86400.0);
4235  }
4236  else
4237  {
4238  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4239  }
4240  }
4241  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4242  {
4243  // added at v0.6 as a safeguard
4244  if(MaxExitSpeed < EntrySpeed)
4245  {
4247  }
4248  // to prevent DeltaExitTimeToMaxInSecs being negative
4249  if(MaxExitSpeed < 1)
4250  {
4251  MaxExitSpeed = 1; // to prevent divide by zero error
4252  }
4253  // below as was
4255  double DeltaExitTimeToMaxInSecs;
4256  double DistanceToMax;
4257  if(PowerAtRail > 1) // added at v2.4.0
4258  {
4259  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4260  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4261  (1.5 * AValue * AValue);
4262  }
4263  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4264  {
4265  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4266  // these not really accurate but will be good enough
4267  DistanceToMax = EntryHalfLength;
4268  }
4269  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4270  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4271  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4272  }
4273  }
4274  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4275  {
4276  // added at v0.6 as a safeguard
4277  if(MaxExitSpeed < EntrySpeed)
4278  {
4280  }
4281  // to prevent DeltaExitTimeToMaxInSecs being negative
4282  if(MaxExitSpeed < 1)
4283  {
4284  MaxExitSpeed = 1; // to prevent divide by zero error
4285  }
4286  // below as was
4288  double DeltaExitTimeToMaxInSecs;
4289  double DistanceToMax;
4290  if(PowerAtRail > 1) // added at v2.4.0
4291  {
4292  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4293  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4294  (1.5 * AValue * AValue);
4295  }
4296  else
4297  {
4298  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4299  // these not really accurate but will be good enough
4300  DistanceToMax = EntryHalfLength / 2;
4301  }
4302  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4303  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4304  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4306  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4307  }
4308  }
4309  }
4310 
4311  else // SPADFlag set
4312  {
4314  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4315  if(ExitSpeedHalfSquared < 10)
4316  {
4317  ExitSpeedHalf = 0;
4318  }
4319  else
4320  {
4321  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4322  }
4323  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4324  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4325  if(ExitSpeedFullSquared < 10)
4326  {
4327  ExitSpeedFull = 0;
4328  }
4329  else
4330  {
4331  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4332  }
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4334 
4335  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4336  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4337  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4338  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4339  // will be the stopping speed.
4340  if(ExitSpeedFull > 0)
4341  {
4342  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4343  {
4344  if((EntryPos == 0) || (EntryPos == 2))
4345  {
4346  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4347  {
4348  ExitPos = 1;
4349  }
4350  else
4351  {
4352  ExitPos = 3;
4353  }
4354  }
4355  else
4356  {
4357  ExitPos = 0;
4358  }
4359  }
4360  else
4361  {
4362  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4363  }
4364  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4365  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4366  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4367  {
4368  int NextElementLength;
4369  if(NextEntryPos > 1)
4370  {
4371  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4372  }
4373  else
4374  {
4375  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4376  }
4377  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4378  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4379  {
4380  ExitSpeedFull = 0;
4381  }
4382  }
4383  }
4384  }
4385  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4386  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4387  {
4388  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4389  if(LeadElement > -1)
4390  {
4392  // don't stop on a continuation either entering or leaving
4393  {
4396  MaxExitSpeed = LimitingSpeed;
4397  BrakeRate = 0;
4398  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4399  // assume length is 50m for ease of calc
4400  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4401  Utilities->CallLogPop(2126);
4402  return;
4403  }
4404  }
4405  else if(MidElement > -1)
4406  {
4408  {
4411  MaxExitSpeed = LimitingSpeed;
4412  BrakeRate = 0;
4413  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4414  // assume length is 50m for ease of calc
4415  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4416  Utilities->CallLogPop(2127);
4417  return;
4418  }
4419  }
4420  else if(LagElement > -1)
4421  {
4423  {
4426  MaxExitSpeed = LimitingSpeed;
4427  BrakeRate = 0;
4428  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4429  // assume length is 50m for ease of calc
4430  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4431  Utilities->CallLogPop(2128);
4432  return;
4433  }
4434  }
4435  if(EntrySpeed > 7.5) // keep going for at least another element
4436  {
4437  ExitSpeedHalf = EntrySpeed - 2.5;
4438  ExitSpeedFull = EntrySpeed - 5;
4439  MaxExitSpeed = LimitingSpeed;
4440  BrakeRate = 0;
4441  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4442  // assume length is 50m for ease of calc
4443  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4444  Utilities->CallLogPop(2129);
4445  return;
4446  }
4447  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4448  {
4449  // will appear to have slowed at steady rate
4450  ExitSpeedHalf = 0;
4451  ExitSpeedFull = 0;
4452  MaxExitSpeed = LimitingSpeed;
4453  BrakeRate = 0;
4454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4456  StoppedWithoutPower = true;
4457  Utilities->CallLogPop(2130);
4458  return;
4459  }
4460  }
4461  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4462  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4463  Utilities->CallLogPop(707);
4464 }
4465 // ---------------------------------------------------------------------------
4466 /*
4467  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4468  {
4469  int NextExitPos;
4470  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4471  if(TimetableVector.empty()) return false;
4472  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4473  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4474  {
4475  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4476  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4477  {
4478  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4479  }
4480  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4481  {
4482  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4483  }
4484  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4485  NextElement = TempElement;
4486  }
4487  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4488  if(NextElement.TrackType == Buffers) return true;
4489  return false;//shouldn't reach here but include to prevent compiler return warning
4490  }
4491 */
4492 // ---------------------------------------------------------------------------
4493 
4494 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4495 /*
4496  returns the number by which the train ActionVectorEntryPtr needs
4497  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4498  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4499  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4500  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4501  or pass (false) the location.
4502 */{
4503  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4504  Stop = false;
4505  if(TimetableFinished || (Name == ""))
4506  {
4507  Utilities->CallLogPop(957);
4508  return(-1);
4509  }
4510  // start looking from current pointer position
4511  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4512  {
4513  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4514  {
4515  break;
4516  }
4517  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4518  {
4519  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4520  {
4521  Stop = true;
4522  Utilities->CallLogPop(960);
4523  return (Ptr - ActionVectorEntryPtr);
4524  }
4525  }
4526  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4527  {
4528  Utilities->CallLogPop(1517);
4529  return (Ptr - ActionVectorEntryPtr);
4530  }
4531  }
4532  Utilities->CallLogPop(959);
4533  return(-1); // not found a valid entry
4534 }
4535 
4536 // ---------------------------------------------------------------------------
4537 
4539 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4540  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4541  Ignores the call-on signal.
4542 */{
4543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4544  int ReturnVal = 0;
4545 
4546  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4547  {
4548  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4549  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4550  }
4551  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4552  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4553 
4554  while(true)
4555  {
4556  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4557  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4558  {
4559  ReturnVal = 1;
4560  break;
4561  }
4562  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4563  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4564  {
4565  ReturnVal = 2;
4566  break;
4567  }
4568  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4569  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4570  {
4571  ReturnVal = 3;
4572  break;
4573  }
4574  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4575  CurrentTrackVectorPosition).TrackType == Crossover))
4576  {
4577  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4578  // must be a loop - reached same point as examined earlier
4579  {
4580  ReturnVal = 4;
4581  break;
4582  }
4583  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4584  {
4585  ReturnVal = 4;
4586  break;
4587  }
4588  }
4589  else
4590  {
4591  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4592  CurrentTrackVectorPosition).TempTrackMarker23))
4593  {
4594  ReturnVal = 4;
4595  break;
4596  }
4597  }
4598  if(EntryPos < 2)
4599  {
4600  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4601  }
4602  else
4603  {
4604  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4605  }
4606  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4607  {
4608  if((EntryPos == 0) || (EntryPos == 2))
4609  {
4610  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4611  {
4612  ExitPos = 1;
4613  }
4614  else
4615  {
4616  ExitPos = 3;
4617  }
4618  }
4619  else
4620  {
4621  ExitPos = 0;
4622  }
4623  }
4624  else
4625  {
4626  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4627  }
4628  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4629  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4630  CurrentTrackVectorPosition = NextTrackVectorPosition;
4631  EntryPos = NextEntryPos;
4632  }
4633  if(ReturnVal == 1)
4634  {
4635  Utilities->CallLogPop(708);
4636  return(false);
4637  }
4638  if(ReturnVal == 2)
4639  {
4640  Utilities->CallLogPop(709);
4641  return(true);
4642  }
4643  if(ReturnVal == 3)
4644  {
4645  Utilities->CallLogPop(946);
4646  return(true);
4647  }
4648  if(ReturnVal == 4)
4649  {
4650  Utilities->CallLogPop(947);
4651  return(true);
4652  }
4653  else
4654  {
4655  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4656  }
4657 }
4658 
4659 // ---------------------------------------------------------------------------
4660 
4662 /*
4663  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4664  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4665  change of direction (cdt), remaining here (Frh), or under signaller control);
4666  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4667  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4668  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4669  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4670  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4671  change points outside the route or have a route conflict if another route is set.
4672 */{
4673  if(Track->RouteFlashFlag)
4674  {
4675  return(false); // don't want to create a new route from the stop signal if one is already in construction as
4676  }
4677  // some of the callingon route elements may be involved
4678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4679  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4680  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4681  int RouteStartPosition;
4682  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4683  int PlatformPosition;
4684  // the track vector position of the first stop platfrom
4685  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4686  // not used here
4687  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4688  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4689  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4690  // must be stopped at a signal but not at a location & still in timetable (a)
4692  // no need to check for SignallerStopped as this function only called in Timetable mode
4693  {
4694  Utilities->CallLogPop(711);
4695  return(false);
4696  }
4697  while(true)
4698  {
4699  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4700  // don't look further than 4km ahead (j)
4701  if(Distance > (4000 + LeadElementDistance))
4702  {
4703  Utilities->CallLogPop(967);
4704  return(false);
4705  }
4706  // if find another train on an element in front, before find a valid platform, return false (c)
4707  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4708  {
4709  Utilities->CallLogPop(713);
4710  return(false);
4711  }
4712  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4713  // be facing later on)
4714  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4715  {
4716  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4717  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4718  if(OtherTrain.LeadElement == -1)
4719  {
4720  Utilities->CallLogPop(714);
4721  return(false);
4722  }
4723  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4724  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4725  {
4726  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4727  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4728  (OtherTrain.TrainMode == Signaller))
4729  {
4730  break;
4731  }
4732  else
4733  {
4734  Utilities->CallLogPop(955);
4735  return(false);
4736  }
4737  }
4738  else // (h)
4739  {
4740  break;
4741  }
4742  }
4743  // if reach buffers or exit continuation return false (can set route)
4744  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4745  {
4746  Utilities->CallLogPop(716);
4747  return(false);
4748  }
4749  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4750  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4752  {
4753  Utilities->CallLogPop(717);
4754  return(false);
4755  }
4756  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4757 /*
4758  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4759  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4760  {
4761  Utilities->CallLogPop(718);
4762  return false;
4763  }
4764 */
4765  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4766  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4767  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4768  {
4769  if(StopRequired)
4770  {
4771  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4772  {
4773  if(!PlatformFoundFlag)
4774  {
4775  PlatformPosition = CurrentTrackVectorPosition;
4776  }
4777  // ensure this only set once at first valid platform position - the unrestricted route will end here
4778  PlatformFoundFlag = true;
4779  }
4780  }
4781  }
4782  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4783  // train has to be at station but that has to be before the next forward signal
4784 /*
4785  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4786  {
4787  Utilities->CallLogPop(719);
4788  return false;
4789  }
4790 */
4791  // make sure points are followed correctly (d) & set ExitPos
4792  if(CurrentTrackElement.TrackType == Points)
4793  {
4794  if((EntryPos == 0) || (EntryPos == 2))
4795  {
4796  if(CurrentTrackElement.Attribute == 0)
4797  {
4798  ExitPos = 1;
4799  }
4800  else
4801  {
4802  ExitPos = 3;
4803  }
4804  }
4805  if(EntryPos == 1)
4806  {
4807  if(CurrentTrackElement.Attribute == 0)
4808  {
4809  ExitPos = 0;
4810  }
4811  else
4812  {
4813  Utilities->CallLogPop(720);
4814  return(false);
4815  }
4816  }
4817  if(EntryPos == 3)
4818  {
4819  if(CurrentTrackElement.Attribute == 1)
4820  {
4821  ExitPos = 0;
4822  }
4823  else
4824  {
4825  Utilities->CallLogPop(721);
4826  return(false);
4827  }
4828  }
4829  }
4830  else
4831  {
4832  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4833  }
4834  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4835  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4836  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4837  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4838  if(ElementNumber < 2)
4839  {
4840  SkipRouteCheck = true;
4841  }
4842  else
4843  {
4844  SkipRouteCheck = false;
4845  }
4846  if(ElementNumber == 1) // the stop signal
4847  {
4848  RouteStartPosition = CurrentTrackVectorPosition;
4849  }
4850 /*
4851  if(ElementNumber == 2)
4852  {
4853  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4854  else AutoSigs = false;
4855  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4856  }
4857 */
4858  if(ElementNumber > 1)
4859  {
4860  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4861  {
4862  RouteOrPartRouteSet = true;
4863  }
4864  else
4865  {
4866  RouteOrPartRouteSet = false;
4867  }
4868  }
4869  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4870  {
4871  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4872  {
4873  Utilities->CallLogPop(1859);
4874  return(false);
4875  }
4876  int ExitLink = CurrentTrackElement.Link[ExitPos];
4877  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4878  {
4879  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4880  {
4881  Utilities->CallLogPop(1850);
4882  return(false);
4883  }
4884  }
4885  }
4886  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4887  if(EntryPos < 2)
4888  {
4889  Distance += CurrentTrackElement.Length01;
4890  }
4891  else
4892  {
4893  Distance += CurrentTrackElement.Length23;
4894  }
4895  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4896  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4897  CurrentTrackVectorPosition = NextTrackVectorPosition;
4898  EntryPos = NextEntryPos;
4899  ElementNumber++;
4900  } // while(true)
4901 
4902  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4903  // from the stop signal (note that it may be last in an autosigs route)
4904  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4905  // this in ClockTimer2)
4906 
4907  // now add elements to the CallonVector
4908  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4909 
4910  AllRoutes->CallonVector.push_back(CallonEntry);
4911  Utilities->CallLogPop(1860);
4912  return(true); // return false if fail to set route for any reason
4913 }
4914 
4915 // ---------------------------------------------------------------------------
4916 /*
4917  bool TTrain::TimetableFinished()
4918  {
4919  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4920  {
4921  return true;
4922  }
4923  return false;
4924  }
4925 */
4926 // ---------------------------------------------------------------------------
4927 
4928 AnsiString TTrain::GetTrainHeadCode(int Caller)
4929 
4930 {
4931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4932  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4933 
4934  Utilities->CallLogPop(1452);
4935  return(RepeatHeadCode);
4936 }
4937 
4938 // ---------------------------------------------------------------------------
4939 
4940 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4941 {
4942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4943  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4944 
4945  Utilities->CallLogPop(1453);
4946  return(RepeatTime);
4947 }
4948 
4949 // ---------------------------------------------------------------------------
4950 
4951 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4952 {
4953  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4955  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4956  // first check that train is fully on the railway
4957  bool FrontValid = false, RearValid = false;
4958  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4959 
4960  if((LeadElement == -1) || (MidElement == -1))
4961  {
4962  TrainToBeJoinedBy = NULL;
4963  Utilities->CallLogPop(2131);
4964  return(false);
4965  }
4967  {
4968  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4969  FrontValid = true;
4970  }
4972  {
4973  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4974  RearValid = true;
4975  }
4976  int TrainToBeJoinedByID = -1;
4977 
4978  // first check if on a 2-track element & select correct ID number if so
4979  if(FrontValid)
4980  {
4981  if(FrontAdjacentTrackElement.TrackType == Bridge)
4982  {
4984  {
4985  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4986  }
4987  else
4988  {
4989  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4990  }
4991  }
4992  else
4993  {
4994  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4995  }
4996  }
4997  if((TrainToBeJoinedByID < 0) && RearValid)
4998  {
4999  // first check if on a 2-track element & select correct ID number if so
5000  if(RearAdjacentTrackElement.TrackType == Bridge)
5001  {
5003  {
5004  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5005  }
5006  else
5007  {
5008  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5009  }
5010  }
5011  else
5012  {
5013  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5014  }
5015  }
5016  if(TrainToBeJoinedByID < 0) // no adjacent train
5017  {
5018  TrainToBeJoinedBy = NULL;
5019  Utilities->CallLogPop(2132);
5020  return(false);
5021  }
5022  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5023  if(!TrainToBeJoinedBy->Stopped())
5024  {
5025  TrainToBeJoinedBy = NULL;
5026  Utilities->CallLogPop(2133);
5027  return(false);
5028  }
5029  Utilities->CallLogPop(2134);
5030  return(true);
5031 }
5032 
5033 // ---------------------------------------------------------------------------
5034 
5035 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5036  TDateTime TimetableNonRepeatTime, bool Warning)
5037 /*
5038  Time = timetable time, the time adjustments for repeat trains is carried out internally
5039  Not all messages need this, if not needed a dummy value is required but not used
5040 
5041  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5042  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5043  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5044  //NB for Frh just give terminated message but without event time - don't use this function
5045  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5046  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5047  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5048  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5049  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5050  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5051  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5052  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5053  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5054  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5055  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5056  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5057  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5058  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5059  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5060  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5061  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5062  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5063  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5064  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5065  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5066  SignallerStop 06:05:40: 2F46 stopped on signaller command
5067  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5068  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5069 */{
5070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5071  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5072  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5073  int IntMinsLate = 0;
5074 
5075  // need to set it in case MinsLate == 0, since it isn't tested for that
5076  if(ActionType == Arrive)
5077  {
5078  ActionLog = " arrived at ";
5079  }
5080  if(ActionType == Terminate)
5081  {
5082  if(TerminatedMessageSent) // to avoid it being sent twice
5083  {
5084  Utilities->CallLogPop(1104);
5085  return;
5086  }
5087  ActionLog = " terminated at ";
5088  TerminatedMessageSent = true;
5089  }
5090  if(ActionType == Depart)
5091  {
5092  ActionLog = " departed from ";
5093  }
5094  if(ActionType == Pass)
5095  {
5096  ActionLog = " passed ";
5097  }
5098  if(ActionType == Create)
5099  {
5100  ActionLog = " created at ";
5101  }
5102  if(ActionType == Enter)
5103  {
5104  ActionLog = " entered railway at ";
5105  }
5106  if(ActionType == Leave)
5107  {
5108  ActionLog = " left railway at ";
5109  }
5110  if(ActionType == FrontSplit)
5111  {
5112  ActionLog = " split from front to ";
5113  }
5114  if(ActionType == RearSplit)
5115  {
5116  ActionLog = " split from rear to ";
5117  }
5118  if(ActionType == JoinedByOther)
5119  {
5120  ActionLog = " joined by ";
5121  }
5122  if(ActionType == ChangeDirection)
5123  {
5124  ActionLog = " changed direction at ";
5125  }
5126  if(ActionType == NewService)
5127  {
5128  ActionLog = " became new service ";
5129  }
5130  if(ActionType == TakeSignallerControl)
5131  {
5132  ActionLog = " taken under signaller control at ";
5133  }
5134  if(ActionType == RestoreTimetableControl)
5135  {
5136  ActionLog = " restored to timetable control at ";
5137  }
5138  if(ActionType == RemoveTrain)
5139  {
5140  if(Crashed)
5141  {
5142  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5143  }
5144  else if(Derailed)
5145  {
5146  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5147  }
5148  else
5149  {
5150  ActionLog = " REMOVED FROM RAILWAY at ";
5151  }
5152  }
5153  if(ActionType == SignallerMoveForwards)
5154  {
5155  ActionLog = " received signaller authority to proceed";
5156  }
5157  if(ActionType == SignallerStepForward)
5158  {
5159  ActionLog = " received signaller authority to step forward";
5160  }
5161  if(ActionType == SignallerChangeDirection)
5162  {
5163  ActionLog = " changed direction under signaller control at ";
5164  }
5165  if(ActionType == SignallerPassRedSignal)
5166  {
5167  ActionLog = " received signaller authority to pass red signal";
5168  }
5169  if(ActionType == SignallerControlStop)
5170  {
5171  ActionLog = " received signaller instruction to stop";
5172  }
5173  if(ActionType == SignallerStop)
5174  {
5175  ActionLog = " stopped on signaller instruction ";
5176  }
5177  if(ActionType == SignallerJoin)
5178  {
5179  ActionLog = " joined under signaller control by ";
5180  }
5181  if(ActionType == TrainFailure)
5182  {
5183  ActionLog = " suffered an onboard power failure at ";
5184  }
5185  if(ActionType == RepairFailedTrain)
5186  {
5187  ActionLog = " failure repaired at ";
5188  }
5189  if(ActionType == SignallerLeave)
5190  {
5191  ActionLog = " left railway under signaller control at ";
5192  }
5193  if(OtherHeadCode != "")
5194  {
5195  OtherHeadCode += " at ";
5196  }
5197  TDateTime ActualTime = TrainController->TTClockTime;
5198 
5199  if(Warning)
5200  {
5201  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5202  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5203  }
5204  else
5205  {
5206  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5207  }
5208  bool TimePerformance = true;
5209 
5210  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5211  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5212  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5213  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5214  // SignallerJoin & RepairFailedTrain new at v2.4.0
5215  {
5216  TimePerformance = false;
5217  }
5218  if(TimePerformance)
5219  {
5220  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5221  MinsDelayed = float(MinsLate);
5222  // new v2.2.0 for OpActionPanel, can be positive or negative
5223  if(ActionType == Arrive)
5224  {
5226  }
5227  // since train has just arrived this value is the
5228  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5229  // subtracted from later stop recoverable times.
5230  if(MinsLate < 0)
5231  {
5232  IntMinsLate = int(ceil(MinsLate));
5233  }
5234  if(MinsLate > 0)
5235  {
5236  IntMinsLate = int(floor(MinsLate));
5237  }
5238  if(IntMinsLate == 0)
5239  {
5240  PerfLog = " on time";
5241  }
5242  else if(IntMinsLate == 1)
5243  {
5244  PerfLog = " 1 minute late";
5245  }
5246  else if(IntMinsLate == -1)
5247  {
5248  PerfLog = " 1 minute early";
5249  }
5250  else if(IntMinsLate > 1)
5251  {
5252  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5253  }
5254  else if(IntMinsLate < -1)
5255  {
5256  int PosIntMinsLate = -IntMinsLate;
5257  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5258  }
5259  if(LocationName.Pos('-') > 0)
5260  {
5261  PerfLog = "," + PerfLog;
5262  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5263  }
5264  Display->PerformanceLog(0, BaseLog + PerfLog);
5265  }
5266  else
5267  {
5268  Display->PerformanceLog(1, BaseLog);
5269  }
5270  if(Warning)
5271  {
5272  Display->WarningLog(0, WarningBaseLog);
5273  }
5274  // update statistics
5275  if((ActionType == Arrive) && (IntMinsLate == 0))
5276  {
5278  }
5279  else if((ActionType == Arrive) && (IntMinsLate > 0))
5280  {
5282  TrainController->TotLateArrMins += IntMinsLate;
5283  }
5284  else if((ActionType == Arrive) && (IntMinsLate < 0))
5285  {
5287  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5288  }
5289 
5290  else if((ActionType == Pass) && (IntMinsLate == 0))
5291  {
5293  }
5294  else if((ActionType == Pass) && (IntMinsLate > 0))
5295  {
5297  TrainController->TotLatePassMins += IntMinsLate;
5298  }
5299  else if((ActionType == Pass) && (IntMinsLate < 0))
5300  {
5302  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5303  }
5304 
5305  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5306  {
5308  }
5309  else if((ActionType == Leave) && (IntMinsLate > 0))
5310  {
5312  TrainController->TotLateExitMins += IntMinsLate;
5313  }
5314  else if((ActionType == Leave) && (IntMinsLate < 0))
5315  {
5317  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5318  }
5319 
5320  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5321  {
5323  }
5324  else if((ActionType == Depart) && (IntMinsLate > 0))
5325  {
5327  TrainController->TotLateDepMins += IntMinsLate;
5328  }
5329  Utilities->CallLogPop(968);
5330 }
5331 
5332 // ---------------------------------------------------------------------------
5333 
5334 void TTrain::TrainHasFailed(int Caller)
5335 {
5336  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5337  if(Crashed || Derailed || DerailPending)
5338  {
5339  TrainFailurePending = false;
5340  Utilities->CallLogPop(2135);
5341  return;
5342  }
5343  AnsiString LocName = "";
5344 
5345  if(LeadElement > -1)
5346  {
5348  }
5349  if((LocName == "") && (MidElement > -1))
5350  {
5352  }
5353  if((LocName == "") && LeadElement > -1)
5354  {
5355  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5356  }
5357  if((LocName == "") && (MidElement > -1))
5358  {
5359  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5360  }
5361  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5362  TrainFailed = true;
5363  TrainFailurePending = false;
5365  PowerAtRail = 0.08;
5366  AValue = sqrt(2 * PowerAtRail / Mass);
5368  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5369  if(Stopped())
5370  {
5371  EntrySpeed = 0;
5372  ExitSpeedHalf = 0;
5373  ExitSpeedFull = 0;
5374  MaxExitSpeed = 0;
5375  BrakeRate = 0;
5376  StoppedWithoutPower = true;
5377  }
5379  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5380  // true for warning, TDateTime not used
5381  Utilities->CallLogPop(2136);
5382 }
5383 
5384 // ---------------------------------------------------------------------------
5385 
5386 void TTrain::FrontTrainSplit(int Caller)
5387 {
5388 /*
5389  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5390  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5391  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5392  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5393 */
5394  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5395  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5396  if(PowerAtRail < 1)
5397  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5398  {
5400  {
5401  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5402  }
5404  Utilities->CallLogPop(2137);
5405  return;
5406  }
5407  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5408 
5409  if(LocationName == "")
5410  {
5411  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5412  }
5413  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5414  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5415  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5417 
5418  // determine all positions & exits
5419  if(LocationName != "")
5420  {
5421  // if message given only call at ~5 sec intervals
5423  {
5424  FirstNamedElementPos = LeadElement;
5425  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5426  // check if possible with LeadElement as First
5427  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5428  {
5429  FirstNamedElementPos = MidElement;
5430  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5431  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5432  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5433  {
5435  {
5436  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5438  }
5439  Utilities->CallLogPop(1009);
5440  return;
5441  }
5442  }
5443  else
5444  {
5445  // if first is possible then check if all 4 positions at location, and if not try the second
5446  int LeadPosA = FirstNamedElementPos;
5447  int LeadPosB = FirstNamedLinkedElementPos;
5448  int LeadPosC = SecondNamedElementPos;
5449  int LeadPosD = SecondNamedLinkedElementPos;
5450  // count number of positions that are at the location
5451  int LeadNumAtLoc = 0;
5452  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5453  {
5454  LeadNumAtLoc++;
5455  }
5456  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5457  {
5458  LeadNumAtLoc++;
5459  }
5460  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5461  {
5462  LeadNumAtLoc++;
5463  }
5464  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5465  {
5466  LeadNumAtLoc++;
5467  }
5468  if(LeadNumAtLoc < 4)
5469  {
5470  FirstNamedElementPos = MidElement;
5471  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5472  SecondNamedLinkedElementPos)) // restore originals
5473  {
5474  FirstNamedElementPos = LeadPosA;
5475  FirstNamedLinkedElementPos = LeadPosB;
5476  SecondNamedElementPos = LeadPosC;
5477  SecondNamedLinkedElementPos = LeadPosD;
5478  }
5479  else // count number at location
5480  {
5481  int MidNumAtLoc = 0;
5482  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5483  {
5484  MidNumAtLoc++;
5485  }
5486  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5487  {
5488  MidNumAtLoc++;
5489  }
5490  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5491  {
5492  MidNumAtLoc++;
5493  }
5494  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5495  {
5496  MidNumAtLoc++;
5497  }
5498  if(LeadNumAtLoc > MidNumAtLoc)
5499  // change back, else keep new values
5500  {
5501  FirstNamedElementPos = LeadPosA;
5502  FirstNamedLinkedElementPos = LeadPosB;
5503  SecondNamedElementPos = LeadPosC;
5504  SecondNamedLinkedElementPos = LeadPosD;
5505  }
5506  }
5507  }
5508  }
5509  }
5510  else
5511  {
5512  Utilities->CallLogPop(1791);
5513  return;
5514  }
5515  }
5516  else
5517  {
5518  throw Exception("Error - LocationName not set in FrontTrainSplit");
5519  }
5520  // set front & rear train parameters
5521  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5522  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5523  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5524  if(LeadElement == FirstNamedElementPos)
5525  {
5526  if(MidElement == SecondNamedElementPos)
5527  {
5528  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5529  FrontTrainRearPosition = FirstNamedElementPos;
5530  RearTrainFrontPosition = SecondNamedElementPos;
5531  RearTrainRearPosition = SecondNamedLinkedElementPos;
5532  }
5533  else // MidElement must == FirstNamedLinkedElementPos
5534  {
5535  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5536  FrontTrainRearPosition = SecondNamedElementPos;
5537  RearTrainFrontPosition = FirstNamedElementPos;
5538  RearTrainRearPosition = FirstNamedLinkedElementPos;
5539  }
5540  }
5541  else // MidElement == FirstNamedElementPos
5542  {
5543  if(LeadElement == SecondNamedElementPos)
5544  {
5545  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5546  FrontTrainRearPosition = SecondNamedElementPos;
5547  RearTrainFrontPosition = FirstNamedElementPos;
5548  RearTrainRearPosition = FirstNamedLinkedElementPos;
5549  }
5550  else // LeadElement must == FirstNamedLinkedElementPos
5551  {
5552  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5553  FrontTrainRearPosition = FirstNamedElementPos;
5554  RearTrainFrontPosition = SecondNamedElementPos;
5555  RearTrainRearPosition = SecondNamedLinkedElementPos;
5556  }
5557  }
5558  RearTrainExitPos = -1;
5559  for(int x = 0; x < 4; x++)
5560  {
5561  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5562  {
5563  RearTrainExitPos = x;
5564  break;
5565  }
5566  }
5567  if(RearTrainExitPos == -1)
5568  {
5569  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5570  }
5571  FrontTrainExitPos = -1;
5572  for(int x = 0; x < 4; x++)
5573  {
5574  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5575  {
5576  FrontTrainExitPos = x;
5577  break;
5578  }
5579  }
5580  if(FrontTrainExitPos == -1)
5581  {
5582  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5583  }
5584  // check no train (apart from self) on any of the 4 elements & fail if so
5585  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5586  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5587 
5588  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5589  {
5590  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5591  }
5592  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5593  {
5594  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5595  }
5596  else
5597  {
5598  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5599  }
5600  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5601  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5602  // can't be a bridge
5603  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5604  // can't be a bridge
5605  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5606  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5607 
5608  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5609  {
5610  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5611  }
5612  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5613  {
5614  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5615  }
5616  else
5617  {
5618  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5619  }
5620  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5621  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5622  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5623  {
5625  {
5628  }
5629  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5630  Utilities->CallLogPop(1010);
5631  return;
5632  }
5634  {
5636  }
5637  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5638  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5639  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5640  // variable as it is needed for setting up the new train
5641  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5642 
5643  UnplotTrain(0);
5644  StartSpeed = 0;
5645  RearStartElement = RearTrainRearPosition;
5646  RearStartExitPos = RearTrainExitPos;
5647  StoppedAtLocation = true;
5648  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5649  {
5650  StoppedWithoutPower = true;
5651  }
5652  PlotStartPosition(3);
5657 
5658  Mass = Mass / 2;
5659  MaxBrakeRate = MaxBrakeRate / 2;
5660  PowerAtRail = PowerAtRail / 2;
5661  AValue = sqrt(2 * PowerAtRail / Mass);
5662  // shouldn't change but include in case not set earlier
5663 
5664  // create new front train
5665 /*
5666  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5667  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5668  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5669 */
5670  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5671  TActionEventType EventType = NoEvent;
5672 
5673  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5674  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5675  // false for SignallerControl
5676  {
5677  Utilities->CallLogPop(1721); // EventType not used here
5678  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5679  // another train, in which case a message will have been sent to the perf log, also might well clear later
5680  // when other train moves away
5681  return;
5682  }
5683  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5684  // see mods in UpdateTrain for v1.3.2
5685  TrainController->TrainAdded = true;
5686 
5687  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5688 
5689  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5690  TTOD.RunningEntry = Running;
5691  Utilities->CallLogPop(998);
5692 }
5693 
5694 // ---------------------------------------------------------------------------
5695 
5696 void TTrain::RearTrainSplit(int Caller)
5697 {
5698 /*
5699  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5700  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5701  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5702  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5703 */
5704  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5705  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5706  if(PowerAtRail < 1)
5707  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5708  {
5710  {
5711  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5712  }
5714  Utilities->CallLogPop(2138);
5715  return;
5716  }
5717  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5718 
5719  if(LocationName == "")
5720  {
5721  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5722  }
5723  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5724  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5725  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5727 
5728  // determine all positions & exits
5729  if(LocationName != "")
5730  {
5731  // if message given only call at ~5 sec intervals
5733  {
5734  FirstNamedElementPos = LeadElement;
5735  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5736  SecondNamedLinkedElementPos))
5737  {
5738  FirstNamedElementPos = MidElement;
5739  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5740  SecondNamedLinkedElementPos))
5741  {
5743  {
5744  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5746  }
5747  Utilities->CallLogPop(1013);
5748  return;
5749  }
5750  }
5751  else
5752  {
5753  // if first is possible then check if all 4 positions at location, and if not try the second
5754  int LeadPosA = FirstNamedElementPos;
5755  int LeadPosB = FirstNamedLinkedElementPos;
5756  int LeadPosC = SecondNamedElementPos;
5757  int LeadPosD = SecondNamedLinkedElementPos;
5758  // count number of positions that are at the location
5759  int LeadNumAtLoc = 0;
5760  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5761  {
5762  LeadNumAtLoc++;
5763  }
5764  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5765  {
5766  LeadNumAtLoc++;
5767  }
5768  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5769  {
5770  LeadNumAtLoc++;
5771  }
5772  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5773  {
5774  LeadNumAtLoc++;
5775  }
5776  if(LeadNumAtLoc < 4)
5777  {
5778  FirstNamedElementPos = MidElement;
5779  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5780  SecondNamedLinkedElementPos)) // restore originals
5781  {
5782  FirstNamedElementPos = LeadPosA;
5783  FirstNamedLinkedElementPos = LeadPosB;
5784  SecondNamedElementPos = LeadPosC;
5785  SecondNamedLinkedElementPos = LeadPosD;
5786  }
5787  else // count number at location
5788  {
5789  int MidNumAtLoc = 0;
5790  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5791  {
5792  MidNumAtLoc++;
5793  }
5794  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5795  {
5796  MidNumAtLoc++;
5797  }
5798  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5799  {
5800  MidNumAtLoc++;
5801  }
5802  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5803  {
5804  MidNumAtLoc++;
5805  }
5806  if(LeadNumAtLoc > MidNumAtLoc)
5807  // change back, else keep new values
5808  {
5809  FirstNamedElementPos = LeadPosA;
5810  FirstNamedLinkedElementPos = LeadPosB;
5811  SecondNamedElementPos = LeadPosC;
5812  SecondNamedLinkedElementPos = LeadPosD;
5813  }
5814  }
5815  }
5816  }
5817  }
5818  else
5819  {
5820  Utilities->CallLogPop(1792);
5821  return;
5822  }
5823  }
5824  else
5825  {
5826  throw Exception("Error - LocationName not set in RearTrainSplit");
5827  }
5828  // set front & rear train parameters
5829  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5830  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5831  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5832  if(LeadElement == FirstNamedElementPos)
5833  {
5834  if(MidElement == SecondNamedElementPos)
5835  {
5836  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5837  FrontTrainRearPosition = FirstNamedElementPos;
5838  RearTrainFrontPosition = SecondNamedElementPos;
5839  RearTrainRearPosition = SecondNamedLinkedElementPos;
5840  }
5841  else // MidElement must == FirstNamedLinkedElementPos
5842  {
5843  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5844  FrontTrainRearPosition = SecondNamedElementPos;
5845  RearTrainFrontPosition = FirstNamedElementPos;
5846  RearTrainRearPosition = FirstNamedLinkedElementPos;
5847  }
5848  }
5849  else // MidElement == FirstNamedElementPos
5850  {
5851  if(LeadElement == SecondNamedElementPos)
5852  {
5853  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5854  FrontTrainRearPosition = SecondNamedElementPos;
5855  RearTrainFrontPosition = FirstNamedElementPos;
5856  RearTrainRearPosition = FirstNamedLinkedElementPos;
5857  }
5858  else // LeadElement must == FirstNamedLinkedElementPos
5859  {
5860  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5861  FrontTrainRearPosition = FirstNamedElementPos;
5862  RearTrainFrontPosition = SecondNamedElementPos;
5863  RearTrainRearPosition = SecondNamedLinkedElementPos;
5864  }
5865  }
5866  RearTrainExitPos = -1;
5867  for(int x = 0; x < 4; x++)
5868  {
5869  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5870  {
5871  RearTrainExitPos = x;
5872  break;
5873  }
5874  }
5875  if(RearTrainExitPos == -1)
5876  {
5877  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5878  }
5879  FrontTrainExitPos = -1;
5880  for(int x = 0; x < 4; x++)
5881  {
5882  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5883  {
5884  FrontTrainExitPos = x;
5885  break;
5886  }
5887  }
5888  if(FrontTrainExitPos == -1)
5889  {
5890  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5891  }
5892  // check no train (apart from self) on any of the 4 elements & fail if so
5893  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5894  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5895 
5896  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5897  {
5898  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5899  }
5900  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5901  {
5902  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5903  }
5904  else
5905  {
5906  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5907  }
5908  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5909  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5910  // can't be a bridge
5911  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5912  // can't be a bridge
5913  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5914  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5915 
5916  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5917  {
5918  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5919  }
5920  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5921  {
5922  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5923  }
5924  else
5925  {
5926  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5927  }
5928  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5929  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5930  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5931  {
5933  {
5936  }
5937  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5938  Utilities->CallLogPop(1014);
5939  return;
5940  }
5942  {
5944  }
5945  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5946  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5947  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5948  // variable as it is needed for setting up the new train
5949  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5950 
5951  UnplotTrain(1);
5952  StartSpeed = 0;
5953  RearStartElement = FrontTrainRearPosition;
5954  RearStartExitPos = FrontTrainExitPos;
5955  StoppedAtLocation = true;
5956  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5957  {
5958  StoppedWithoutPower = true;
5959  }
5960  PlotStartPosition(4);
5965  Mass = Mass / 2;
5966  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5967  MaxBrakeRate = MaxBrakeRate / 2;
5968  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5969  PowerAtRail = PowerAtRail / 2;
5970  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5971  AValue = sqrt(2 * PowerAtRail / Mass);
5972  // shouldn't change but include in case not set earlier
5973 
5974  // create new rear train
5975 /*
5976  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5977  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5978  int RepeatNumber, int IncrementalMinutes)
5979 */
5980  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5981  TActionEventType EventType = NoEvent;
5982 
5983  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5984  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5985  // false for SignallerControl
5986  {
5987  Utilities->CallLogPop(1722); // EventType not used here
5988  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5989  // another train, in which case a message will have been sent to the perf log, also might well clear later
5990  // when other train moves away
5991  return;
5992  }
5993  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5994  // see mods in UpdateTrain for v1.3.2
5995  TrainController->TrainAdded = true;
5996  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5997 
5998  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5999  TTOD.RunningEntry = Running;
6000  Utilities->CallLogPop(1015);
6001 }
6002 
6003 // ---------------------------------------------------------------------------
6004 
6005 void TTrain::FinishJoin(int Caller)
6006 {
6007  if(FinishJoinLogSent == false)
6008  {
6009  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6010  FinishJoinLogSent = true; // so don't keep logging this event
6011  // don't need to reset it to false after the event as the train is deleted
6012  }
6013  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6014  if(TrainFailed)
6015  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6016  {
6018  {
6019  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6020  }
6022  Utilities->CallLogPop(2139);
6023  return;
6024  }
6025  if(TrainGone)
6026  // this means that the train has already joined the other & is awaiting deletion by TrainController
6027  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6028  // on from jbo & TrainToJoinIsAdjacent returns false
6029  {
6030  Utilities->CallLogPop(1035);
6031  return;
6032  }
6033  TTrain *TrainToJoin;
6035 
6036  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6037  {
6039  {
6040  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6043  }
6044  Utilities->CallLogPop(1030);
6045  return; // keep this here in case need to add code before final return
6046  }
6047  // no need to clear error report flag here, cleared in jbo function
6048  // No need to set TimetableFinished, done in jbo function
6049  Utilities->CallLogPop(1031);
6050 }
6051 
6052 // ---------------------------------------------------------------------------
6053 
6054 void TTrain::JoinedBy(int Caller)
6055 {
6056  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6058  if(PowerAtRail < 1)
6059  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6060  {
6062  {
6063  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6064  }
6066  Utilities->CallLogPop(2140);
6067  return;
6068  }
6069  TTrain *TrainToBeJoinedBy;
6071 
6072  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6073  {
6075  {
6076  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6079  }
6080  LastActionDelayFlag = true;
6081  // need to update LastActionTime if this train first to arrive as need 30s after
6082  // both adjacent before the join
6083  Utilities->CallLogPop(1032);
6084  return;
6085  }
6086  // here when other train is adjacent
6088  {
6090  // need to update this as need 30s after both adjacent before the join
6091  LastActionDelayFlag = false;
6092  Utilities->CallLogPop(1033);
6093  return;
6094  }
6095  // here when other train is adjacent & 30 secs elapsed since both adjacent
6096 
6097  // set new values for mass etc
6098  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6099  {
6100  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6101  }
6102  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6103  double OwnBrakeForce = MaxBrakeRate * Mass;
6104  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6105 
6106  Mass += TrainToBeJoinedBy->Mass;
6107  MaxBrakeRate = CombinedBrakeRate;
6108  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6109  AValue = sqrt(2 * PowerAtRail / Mass);
6110 
6112  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6113  TrainToBeJoinedBy->TimetableFinished = true;
6114  TrainToBeJoinedBy->TrainGone = true;
6115  // this will cause other train to be deleted
6116  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6120  Utilities->CallLogPop(1034);
6121 }
6122 
6123 // ---------------------------------------------------------------------------
6124 
6126 {
6127  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6128  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6129  if(PowerAtRail < 1)
6130  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6131  {
6133  {
6134  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6135  }
6136  ZeroPowerNoCDTMessage = true;
6137  Utilities->CallLogPop(2141);
6138  return;
6139  }
6140  TColor TempColour = BackgroundColour;
6141 
6142  UnplotTrain(2);
6145  StartSpeed = 0;
6146  StoppedAtLocation = true;
6147  PlotStartPosition(1);
6148  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6149  // plot same as was - should always be pale green
6153 
6154  //now erase a stub route if there is one, added at v2.5.1
6155  //first element of route is now immediately behind the train (i.e. next to MidElement)
6156  if(MidEntryPos >= 0)
6157  {
6158  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6159  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6160  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6161  int RouteNumber = -1;
6162  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6163  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6164  {
6165  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6166  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6167  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6168  {
6169  bool FirstPass = true; //added at v2.8.0
6170  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6171  {
6172  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6173  int TVPos2 = PDE.GetTrackVectorPosition();
6174  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6175  {
6176  break;
6177  }
6178  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6180  {
6181  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6182  }
6183  else
6184  {
6185  break;
6186  }
6187  FirstPass = false;
6188  }
6189  AllRoutes->RebuildRailwayFlag = true;
6190  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6191  }
6192  }
6193  }
6194  Utilities->CallLogPop(1012);
6195 }
6196 
6197 // ---------------------------------------------------------------------------
6198 
6199 void TTrain::NewTrainService(int Caller)
6200 // change to new train, give new service message
6201 {
6202  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6203  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6204  if(PowerAtRail < 1)
6205  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6206  {
6208  {
6209  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6210  }
6212  Utilities->CallLogPop(2142);
6213  return;
6214  }
6216 
6218  UnplotTrain(3);
6221  StartSpeed = 0;
6226  HeadCode = NewHeadCode;
6227  StoppedAtLocation = true;
6228  PlotStartPosition(5);
6230  // pale green
6233  TerminatedMessageSent = false;
6234  Utilities->CallLogPop(1022);
6235 }
6236 
6237 // ---------------------------------------------------------------------------
6238 
6239 void TTrain::RemainHere(int Caller)
6240 {
6241  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6242  if(RemainHereLogNotSent) // to prevent repeated logs
6243  {
6244  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6245  RemainHereLogNotSent = false;
6246  }
6248  {
6249  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6252  TerminatedMessageSent = true;
6253  }
6254  TimetableFinished = true;
6255  Utilities->CallLogPop(1023);
6256 }
6257 
6258 // ---------------------------------------------------------------------------
6259 
6260 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6261 /*
6262  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6263  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6264  except where an action is a departure, starting at the current value for the pointer
6265  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6266  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6267 */{
6268  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6269  {
6270  return; // if remove train that starts under signaller control no messages needed
6271 
6272  }
6273  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6274  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6275  if(IncNum > 0)
6276  {
6277  for(int x = 0; x < IncNum; x++)
6278  {
6279  if(x > 0)
6280  {
6281  Ptr++;
6282  }
6283  // arrival - no need to test for termination as this section only covers missed actions up to the
6284  // arrival point - may terminate later but that not missed
6285  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6286  {
6288  }
6289  // arrival & departure
6290  if(Ptr->FormatType == TimeTimeLoc)
6291  {
6293  }
6294  // departure
6295  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6296  {
6297  continue; // skip TimeLoc departures, message given for arrivals
6298  }
6299  // pass
6300  else if(Ptr->FormatType == PassTime)
6301  {
6303  }
6304  // split
6305  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6306  {
6308  }
6309  // jbo
6310  else if(Ptr->Command == "jbo")
6311  {
6313  }
6314  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6315  // be starts, finishes or cdt
6316  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6317  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6318  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6319  (Ptr->FormatType == Repeat))
6320  {
6321  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6322  }
6323  }
6324  }
6325  else
6326  {
6327  bool IncludeFER = false;
6328  if(IncNum == -1)
6329  {
6330  IncludeFER = true;
6331  }
6332  while(true) // finish commands & repeats break out of loop
6333  {
6334  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6335  if(!IncludeFER && (Ptr->Command == "Fer"))
6336  {
6337  break;
6338  }
6339  // Fer & included
6340  else if(IncludeFER && (Ptr->Command == "Fer"))
6341  {
6343  break;
6344  }
6345  // Repeat
6346  else if(Ptr->FormatType == Repeat)
6347  {
6348  break;
6349  }
6350  // Fjo
6351  else if(Ptr->Command == "Fjo")
6352  {
6354  break;
6355  }
6356  // Frh
6357  else if(Ptr->Command == "Frh")
6358  {
6360  {
6362  TerminatedMessageSent = true;
6363  }
6364  break;
6365  }
6366  // Frh-sh
6367  else if(Ptr->Command == "Frh-sh")
6368  {
6370  {
6372  TerminatedMessageSent = true;
6373  }
6374  break;
6375  }
6376  // Fns, F-nshs, Fns-sh
6377  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6378  {
6380  break;
6381  }
6382  // end of breakout actions
6383  // arrival
6384  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6385  {
6386  if(IsTrainTerminating(1))
6387  {
6389  TerminatedMessageSent = true;
6390  }
6391  else
6392  {
6394  }
6395  }
6396  // arrival & departure
6397  else if(Ptr->FormatType == TimeTimeLoc)
6398  {
6400  }
6401  // departure
6402  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6403  {
6404  Ptr++;
6405  continue; // skip TimeLoc departures, message given for arrivals
6406  }
6407  // pass
6408  else if(Ptr->FormatType == PassTime)
6409  {
6411  }
6412  // split
6413  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6414  {
6416  }
6417  // jbo
6418  else if(Ptr->Command == "jbo")
6419  {
6421  }
6422  // cdt
6423  else if(Ptr->Command == "cdt")
6424  {
6426  }
6427  // Errors
6428  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6429  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6430  {
6431  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6432  }
6433  Ptr++;
6434  }
6435  TimetableFinished = true;
6436  }
6437  Utilities->CallLogPop(1021);
6438 }
6439 
6440 // ---------------------------------------------------------------------------
6441 
6442 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6443 // ensure same repeatnumber
6444 {
6445  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6447 
6448  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6449  {
6450  Utilities->CallLogPop(1024);
6451  return(false);
6452  }
6453  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6454  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6455  {
6456  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6457  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6458  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6459  {
6460  Utilities->CallLogPop(1025);
6461  return(true);
6462  }
6463  }
6464  Utilities->CallLogPop(1026);
6465  return(false);
6466 }
6467 
6468 // ---------------------------------------------------------------------------
6469 
6470 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6471 // ensure same repeatnumber
6472 {
6473  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6474  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6475 
6476  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6477  {
6478  Utilities->CallLogPop(1027);
6479  return(false);
6480  }
6481  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6482  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6483  {
6484  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6485  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6486  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6487  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6488  {
6489  Utilities->CallLogPop(1028);
6490  return(true);
6491  }
6492  }
6493  Utilities->CallLogPop(1029);
6494  return(false);
6495 }
6496 
6497 // ---------------------------------------------------------------------------
6498 
6500 {
6501  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6503  if(PowerAtRail < 1)
6504  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6505  {
6507  {
6508  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6509  }
6511  Utilities->CallLogPop(2143);
6512  return;
6513  }
6514  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6515 
6517  UnplotTrain(4);
6520  StartSpeed = 0;
6525  HeadCode = NewHeadCode;
6526  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6527  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6528  StoppedAtLocation = true;
6529  PlotStartPosition(6);
6531  // pale green
6534  TerminatedMessageSent = false;
6535  Utilities->CallLogPop(1078);
6536 }
6537 
6538 // ---------------------------------------------------------------------------
6539 
6541 // need to check whether all repeats finished or not
6542 {
6543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6544  if(RemainHereLogNotSent) // to prevent repeated logs
6545  {
6546  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6547  RemainHereLogNotSent = false;
6548  }
6550  // finished all repeats
6551  {
6553  {
6556  TerminatedMessageSent = true;
6557  // no need to clear message as no more actions
6558  }
6559  TimetableFinished = true;
6560  Utilities->CallLogPop(1080);
6561  return;
6562  }
6563  if(PowerAtRail < 1)
6564  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6565  {
6567  {
6568  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6569  }
6571  Utilities->CallLogPop(2144);
6572  return;
6573  }
6574  int TempRepeatNumber = RepeatNumber + 1;
6575  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6576  // until after LogAction or the wrong time will be used
6577  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6578 
6580  RepeatNumber++;
6581  UnplotTrain(5);
6584  StartSpeed = 0;
6589  HeadCode = NewHeadCode;
6590  StoppedAtLocation = true;
6591  PlotStartPosition(7);
6593  // pale green
6596  TerminatedMessageSent = false;
6597  Utilities->CallLogPop(1079);
6598 }
6599 
6600 // ---------------------------------------------------------------------------
6601 
6603 {
6604  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6605  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6606  if(PowerAtRail < 1)
6607  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6608  {
6610  {
6611  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6612  }
6614  Utilities->CallLogPop(2145);
6615  return;
6616  }
6618  // finished all repeats
6619  {
6620  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6622  RepeatNumber = 0;
6623  UnplotTrain(6);
6626  StartSpeed = 0;
6628  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6629  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6631  HeadCode = NewHeadCode;
6632  StoppedAtLocation = true;
6633  PlotStartPosition(9);
6637  TerminatedMessageSent = false;
6638  Utilities->CallLogPop(1081);
6639  return;
6640  }
6641  int TempRepeatNumber = RepeatNumber + 1;
6642  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6643  // until after LogAction or the wrong time will be used
6644  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6645 
6647  RepeatNumber++;
6648  UnplotTrain(7);
6651  StartSpeed = 0;
6656  HeadCode = NewHeadCode;
6657  StoppedAtLocation = true;
6658  PlotStartPosition(8);
6660  // pale green
6663  TerminatedMessageSent = false;
6664  Utilities->CallLogPop(1082);
6665 }
6666 
6667 // ---------------------------------------------------------------------------
6668 
6670 {
6671  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6672  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6673  // must be preceded by a TimeLoc departure
6674  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6675  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6676  {
6678  {
6679  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6680  {
6681  Utilities->CallLogPop(1083);
6682  return(false);
6683  }
6684  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6685  {
6686  Utilities->CallLogPop(1084);
6687  return(true);
6688  }
6689  }
6690  }
6691  Utilities->CallLogPop(1085);
6692  return(false);
6693 }
6694 
6695 // ---------------------------------------------------------------------------
6696 
6697 bool TTrain::AbleToMove(int Caller)
6698 {
6699  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6700  bool Able = true;
6701 
6702  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6703  {
6704  // StoppedForTrainInFront removed as tested below
6705  Able = false;
6706  Utilities->CallLogPop(2146); // added v2.4.0
6707  return(Able); // added v2.4.0
6708  }
6709  if(LeadElement > -1)
6710  {
6711  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6712  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6713  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6714  {
6715  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6716  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6717  {
6718  Able = true;
6719  StoppedForTrainInFront = false;
6720  }
6721  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6722  {
6723  Able = true;
6724  StoppedForTrainInFront = false;
6725  }
6726  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6727  {
6728  Able = true;
6729  StoppedForTrainInFront = false;
6730  }
6731  }
6732  else
6733  {
6735  {
6736  Able = false;
6737  }
6738  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6739  }
6740  }
6741  else // leaving at a continuation so keep going
6742  {
6743  Able = true;
6744  StoppedForTrainInFront = false;
6745  }
6746  Utilities->CallLogPop(1454);
6747  return(Able);
6748 }
6749 
6750 // ---------------------------------------------------------------------------
6751 
6753 {
6754  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6755  // won't be set; if there is a train then set StoppedForTrainInFront
6756  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6757  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6758  if(LeadElement == -1) // exiting at continuation
6759  {
6760  Utilities->CallLogPop(2045);
6761  return(false);
6762  }
6763  // end of addition
6764  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6765  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6766 
6767  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6768  {
6769  StoppedForTrainInFront = true;
6770  Utilities->CallLogPop(1455);
6771  return(false);
6772  }
6773  else
6774  {
6775  Utilities->CallLogPop(1456);
6777  // StoppedWithoutPower added v2.4.0
6778  }
6779 }
6780 
6781 // ---------------------------------------------------------------------------
6782 
6784 {
6785  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6786  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6787  TColor TempColour = BackgroundColour;
6788 
6789  UnplotTrain(8);
6792  StartSpeed = 0;
6793  PlotStartPosition(2);
6794  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6795 
6796  //now erase a stub route if there is one, added at v2.5.1
6797  //first element of route is now immediately behind the train (i.e. next to MidElement)
6798  if(MidEntryPos >= 0)
6799  {
6800  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6801  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6802  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6803  int RouteNumber = -1;
6804  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6805  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6806  {
6807  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6808  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6809  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6810  {
6811  bool FirstPass = true; //added at v2.8.0
6812  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6813  {
6814  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6815  int TVPos2 = PDE.GetTrackVectorPosition();
6816  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6817  {
6818  break;
6819  }
6820  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6822  {
6823  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6824  }
6825  else
6826  {
6827  break;
6828  }
6829  FirstPass = false;
6830  }
6831  AllRoutes->RebuildRailwayFlag = true;
6832  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6833  }
6834  }
6835  }
6836  Utilities->CallLogPop(1102);
6837 }
6838 
6839 // ---------------------------------------------------------------------------
6840 
6842 {
6843  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6844  ",FloatingLabelNextString" + "," + HeadCode);
6845  AnsiString RetStr = "", LocationName = "";
6846 
6847  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6848  {
6849  throw Exception("Error - start entry in FloatingLabelNextString");
6850  }
6851  if(Ptr->FormatType == TimeTimeLoc)
6852  {
6853  if(TrainMode == Timetable)
6854  {
6855  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6856  // not arrived yet in tt mode
6857  {
6858  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6859  }
6860  else
6861  {
6862  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6863  }
6864  }
6865  else // TrainMode == Signaller
6866  {
6867  if(!DepartureTimeSet) // not arrived yet
6868  {
6869  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6870  }
6871  else
6872  {
6873  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6874  }
6875  }
6876  }
6877  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6878  {
6879  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6880  }
6881  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6882  {
6883  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6884  }
6885  else if(Ptr->FormatType == PassTime) // new
6886  {
6887  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6888  }
6889  else if(Ptr->Command == "Fns")
6890  {
6891  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6892  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6893  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6894  }
6895  else if(Ptr->Command == "F-nshs")
6896  {
6897  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6899  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6900  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6901  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6902  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6903  }
6904  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6905  {
6906  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6907  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6908  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6909  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6910  }
6911  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6912  {
6913  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6914  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6915  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6916  }
6917  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6918  {
6919  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6920  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6921  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6922  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6923  }
6924  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6925  {
6926  RetStr ="None, train terminated at " + Ptr->LocationName;
6927  }
6928  else if(Ptr->Command == "Frh")
6929  {
6930  RetStr = "None, train terminated at " + Ptr->LocationName;
6931  }
6932  else if(Ptr->Command == "Fer")
6933  {
6934  AnsiString AllowedExits = "";
6935  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6936  }
6937  else if(Ptr->Command == "Fjo")
6938  {
6939  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6941  }
6942  else if(Ptr->Command == "jbo")
6943  {
6944  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6945  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6946  }
6947  else if(Ptr->Command == "fsp")
6948  {
6949  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6950  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6951  }
6952  else if(Ptr->Command == "rsp")
6953  {
6954  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6955  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6956  }
6957  else if(Ptr->Command == "cdt")
6958  {
6959  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6960  }
6961  Utilities->CallLogPop(1124);
6962  return(RetStr);
6963 }
6964 
6965 // ---------------------------------------------------------------------------
6966 
6967 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6968 {
6969  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6970  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6971  AnsiString DepTime = "", EventTime = "";
6972  bool CDTFlag = false; //reports if train changes direction before departs
6973  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6974  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6975  {
6976  if(AVI->Command == "cdt")
6977  {
6978  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6979  continue;
6980  }
6981  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6982  {
6983  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6984  RetStr += "\nNew service splits at " + EventTime;
6985  Utilities->CallLogPop(2234);
6986  return(RetStr);
6987  }
6988  if(AVI->Command == "jbo")
6989  {
6990  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6991  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6992  Utilities->CallLogPop(2235);
6993  return(RetStr);
6994  }
6995  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6996  {
6997  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6998  if(CDTFlag)
6999  {
7000  RetStr += "\nNew service changes direction then departs at " + DepTime;
7001  }
7002  else
7003  {
7004  RetStr += "\nNew service departs at " + DepTime;
7005  }
7006  Utilities->CallLogPop(2236);
7007  return(RetStr);
7008  }
7009  }
7010  Utilities->CallLogPop(2208);
7011  return(RetStr); //if reach here then RetStr doesn't change
7012 }
7013 
7014 // ---------------------------------------------------------------------------
7015 
7017 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7018 {
7019  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7020  ",FloatingTimetableString" + "," + HeadCode);
7021  AnsiString RetStr = "", PartStr = "";
7022  int Count = 0;
7023 
7024  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7025  // can start in signaller control so exclude this
7026  {
7027  throw Exception("Error - start entry in FloatingTimetableString");
7028  }
7029  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
7030  bool FirstPass = true;
7031  Ptr--; // because incremented at start of loop
7032 
7033  // different first TimeTimeLoc display if in signaller control
7034  do
7035  {
7036  Ptr++;
7037  if((Ptr->FormatType == Repeat) || TimetableFinished)
7038  {
7039  break;
7040  }
7041  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7042  {
7043  AnsiString TrainLoc = "";
7044  if(TrainMode == Timetable)
7045  {
7046  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7047  {
7048  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7049  }
7050  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7051  {
7052  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7053  }
7054  else
7055  {
7056  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7057  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7058  Count++; // because there are 2 entries
7059  }
7060  }
7061  else // TrainMode == Signaller
7062  {
7063  if(DepartureTimeSet)
7064  {
7065  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7066  }
7067  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7068  {
7069  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7070  }
7071  else
7072  {
7073  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7074  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7075  Count++; // because there are 2 entries
7076  }
7077  }
7078  }
7079  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7080  {
7081  AnsiString TrainLoc = "";
7082  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7083  {
7084  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7085  }
7086  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7087  {
7088  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7089  }
7090  else
7091  {
7092  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7093  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7094  Count++; // because there are 2 entries
7095  }
7096  }
7097  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7098  {
7099  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7100  }
7101  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7102  {
7103  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7104  }
7105  else if(Ptr->FormatType == PassTime) // new
7106  {
7107  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7108  }
7109  else if(Ptr->Command == "Fns")
7110  {
7111  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7112  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7113  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7114  }
7115  else if(Ptr->Command == "F-nshs")
7116  {
7117  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7118  Ptr->LocationName;
7119  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7120  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7121  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7122  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7123  }
7124  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7125  {
7126  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7127  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7128  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7129  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7130  }
7131  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7132  {
7133  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7134  +" at " + Ptr->LocationName;
7135  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7136  }
7137  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7138  {
7139  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7140  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7141  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7142  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7143  }
7144  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7145  {
7146  PartStr = "Terminate at " + Ptr->LocationName;
7147  }
7148  else if(Ptr->Command == "Frh")
7149  {
7150  PartStr = "Terminate at " + Ptr->LocationName;
7151  }
7152  else if(Ptr->Command == "Fer")
7153  {
7154  AnsiString AllowedExits = "";
7155  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7156  }
7157  else if(Ptr->Command == "Fjo")
7158  {
7159  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7160  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7161  }
7162  else if(Ptr->Command == "jbo")
7163  {
7164  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7165  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7166  }
7167  else if(Ptr->Command == "fsp")
7168  {
7169  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7170  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7171  }
7172  else if(Ptr->Command == "rsp")
7173  {
7174  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7175  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7176  }
7177  else if(Ptr->Command == "cdt")
7178  {
7179  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7180  }
7181  if(RetStr != "")
7182  {
7183  RetStr = RetStr + '\n' + PartStr;
7184  }
7185  else
7186  {
7187  RetStr = PartStr;
7188  }
7189  FirstPass = false;
7190  Count++;
7191  }
7192  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7193  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7194  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7195  // forward as anyone should wish to see without looking at the full timetable
7196  if(TimetableFinished)
7197  {
7198  if(TrainMode == Timetable)
7199  {
7200  RetStr = "Timetable finished";
7201  }
7202  else
7203  {
7204  RetStr = "No timetable";
7205  }
7206  }
7207  Utilities->CallLogPop(1125);
7208  return(RetStr);
7209 }
7210 
7211 // ---------------------------------------------------------------------------
7212 
7213 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7214 {
7215  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7216  Utilities->SaveFileString(OutFile, HeadCode);
7219  Utilities->SaveFileInt(OutFile, StartSpeed);
7222  Utilities->SaveFileInt(OutFile, RepeatNumber);
7225  Utilities->SaveFileInt(OutFile, Mass);
7228  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7235  Utilities->SaveFileDouble(OutFile, BrakeRate);
7239  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7240  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7241  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7242  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7243  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7244  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7248  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7253  Utilities->SaveFileBool(OutFile, Derailed);
7255  Utilities->SaveFileBool(OutFile, Crashed);
7262  Utilities->SaveFileBool(OutFile, NotInService);
7263  Utilities->SaveFileBool(OutFile, Plotted);
7264  Utilities->SaveFileBool(OutFile, TrainGone);
7265  Utilities->SaveFileBool(OutFile, SPADFlag);
7267  Utilities->SaveFileInt(OutFile, HOffset[0]);
7268  Utilities->SaveFileInt(OutFile, HOffset[1]);
7269  Utilities->SaveFileInt(OutFile, HOffset[2]);
7270  Utilities->SaveFileInt(OutFile, HOffset[3]);
7271  Utilities->SaveFileInt(OutFile, VOffset[0]);
7272  Utilities->SaveFileInt(OutFile, VOffset[1]);
7273  Utilities->SaveFileInt(OutFile, VOffset[2]);
7274  Utilities->SaveFileInt(OutFile, VOffset[3]);
7275  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7276  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7277  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7278  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7279  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7280  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7281  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7282  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7284  Utilities->SaveFileInt(OutFile, (short)Straddle);
7285  Utilities->SaveFileInt(OutFile, NextTrainID);
7286  Utilities->SaveFileInt(OutFile, TrainID);
7287  Utilities->SaveFileInt(OutFile, LeadElement);
7288  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7289  Utilities->SaveFileInt(OutFile, LeadExitPos);
7290  Utilities->SaveFileInt(OutFile, MidElement);
7291  Utilities->SaveFileInt(OutFile, MidEntryPos);
7292  Utilities->SaveFileInt(OutFile, MidExitPos);
7293  Utilities->SaveFileInt(OutFile, LagElement);
7294  Utilities->SaveFileInt(OutFile, LagEntryPos);
7295  Utilities->SaveFileInt(OutFile, LagExitPos);
7296  int ColourNumber;
7297 
7299  {
7300  ColourNumber = 0;
7301  }
7303  {
7304  ColourNumber = 1;
7305  }
7307  {
7308  ColourNumber = 2;
7309  }
7311  {
7312  ColourNumber = 3;
7313  }
7315  {
7316  ColourNumber = 4;
7317  }
7319  {
7320  ColourNumber = 5;
7321  }
7323  {
7324  ColourNumber = 6;
7325  }
7327  {
7328  ColourNumber = 7;
7329  }
7331  {
7332  ColourNumber = 8;
7333  }
7335  {
7336  ColourNumber = 9;
7337  }
7339  {
7340  ColourNumber = 10;
7341  }
7343  {
7344  ColourNumber = 11;
7345  }
7347  {
7348  ColourNumber = 12;
7349  }
7350  else if(BackgroundColour == clTRSBackground)
7351  {
7352  ColourNumber = 13;
7353  }
7355  {
7356  ColourNumber = 14; // added at v2.4.0
7357  }
7358  Utilities->SaveFileInt(OutFile, ColourNumber);
7359 
7360  // additional data
7361  bool ForwardHeadCode;
7362 
7363  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7364  {
7365  ForwardHeadCode = true;
7366  }
7367  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7368  else
7369  {
7370  ForwardHeadCode = false;
7371  }
7372  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7373 
7374  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7375 
7376  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7377  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7378 
7379  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7380  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7381  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7382  // so use the last asterisk position for this - 0 for false & 1 for true
7383  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7384  AnsiString Marker;
7385 
7387  {
7388  Marker = "*****1";
7389  }
7390  else
7391  {
7392  Marker = "*****0";
7393  }
7394  if(RestoreTimetableLocation == "")
7395  {
7396  Utilities->SaveFileString(OutFile, Marker);
7397  }
7398  else
7399  {
7400  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7401  Utilities->SaveFileString(OutFile, CombinedString);
7402  // RestoreTimetableLocation + marker
7403  }
7404  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7405  Utilities->CallLogPop(1457);
7406 }
7407 
7408 // ---------------------------------------------------------------------------
7409 
7410 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7411 {
7412  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7413  HeadCode = Utilities->LoadFileString(InFile);
7416  StartSpeed = Utilities->LoadFileInt(InFile);
7418  if(SignallerMaxSpeed < 10)
7419  {
7420  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7421  }
7423  RepeatNumber = Utilities->LoadFileInt(InFile);
7426  Mass = Utilities->LoadFileInt(InFile);
7429  {
7431  }
7432  // above added at v2.1.0 for legacy session files where value may not have been limited
7434  EntrySpeed = Utilities->LoadFileDouble(InFile);
7438  if(TimetableMaxRunningSpeed < 10)
7439  {
7440  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7441  }
7443  if(MaxRunningSpeed < 10)
7444  {
7445  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7446  }
7449  BrakeRate = Utilities->LoadFileDouble(InFile);
7453  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7454  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7455  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7456  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7457  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7458  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7467  Derailed = Utilities->LoadFileBool(InFile);
7469  Crashed = Utilities->LoadFileBool(InFile);
7476  NotInService = Utilities->LoadFileBool(InFile);
7477  Plotted = Utilities->LoadFileBool(InFile);
7478  TrainGone = Utilities->LoadFileBool(InFile);
7479  SPADFlag = Utilities->LoadFileBool(InFile);
7481  HOffset[0] = Utilities->LoadFileInt(InFile);
7482  HOffset[1] = Utilities->LoadFileInt(InFile);
7483  HOffset[2] = Utilities->LoadFileInt(InFile);
7484  HOffset[3] = Utilities->LoadFileInt(InFile);
7485  VOffset[0] = Utilities->LoadFileInt(InFile);
7486  VOffset[1] = Utilities->LoadFileInt(InFile);
7487  VOffset[2] = Utilities->LoadFileInt(InFile);
7488  VOffset[3] = Utilities->LoadFileInt(InFile);
7489  PlotElement[0] = Utilities->LoadFileInt(InFile);
7490  PlotElement[1] = Utilities->LoadFileInt(InFile);
7491  PlotElement[2] = Utilities->LoadFileInt(InFile);
7492  PlotElement[3] = Utilities->LoadFileInt(InFile);
7493  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7494  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7495  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7496  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7498  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7499  NextTrainID = Utilities->LoadFileInt(InFile);
7500  // will be same for all but best to save all anyway
7501  TrainID = Utilities->LoadFileInt(InFile);
7502  LeadElement = Utilities->LoadFileInt(InFile);
7503  LeadEntryPos = Utilities->LoadFileInt(InFile);
7504  LeadExitPos = Utilities->LoadFileInt(InFile);
7505  MidElement = Utilities->LoadFileInt(InFile);
7506  MidEntryPos = Utilities->LoadFileInt(InFile);
7507  MidExitPos = Utilities->LoadFileInt(InFile);
7508  LagElement = Utilities->LoadFileInt(InFile);
7509  LagEntryPos = Utilities->LoadFileInt(InFile);
7510  LagExitPos = Utilities->LoadFileInt(InFile);
7511  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7512 
7513  if(ColourNumber == 0)
7514  {
7516  }
7517  else if(ColourNumber == 1)
7518  {
7520  }
7521  else if(ColourNumber == 2)
7522  {
7524  }
7525  else if(ColourNumber == 3)
7526  {
7528  }
7529  else if(ColourNumber == 4)
7530  {
7532  }
7533  else if(ColourNumber == 5)
7534  {
7536  }
7537  else if(ColourNumber == 6)
7538  {
7540  }
7541  else if(ColourNumber == 7)
7542  {
7544  }
7545  else if(ColourNumber == 8)
7546  {
7548  }
7549  else if(ColourNumber == 9)
7550  {
7552  }
7553  else if(ColourNumber == 10)
7554  {
7556  }
7557  else if(ColourNumber == 11)
7558  {
7560  }
7561  else if(ColourNumber == 12)
7562  {
7564  }
7565  else if(ColourNumber == 13)
7566  {
7568  }
7569  else if(ColourNumber == 14)
7570  {
7571  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7572 
7573  }
7574  // additional data
7576  // sets the BackgroundColour to the loaded value
7577  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7578 
7579  if(ForwardHeadCode)
7580  {
7581  for(int x = 0; x < 4; x++)
7582  {
7584  }
7585  }
7586  else
7587  {
7588  for(int x = 0; x < 4; x++)
7589  {
7590  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7591  }
7592  }
7593  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7594  if(TrainMode == Timetable)
7595  {
7596  if(Crashed)
7597  {
7599  }
7600  else
7601  {
7603  }
7604  }
7605  else
7606  {
7608  }
7610  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7611  if(Straddle == LeadMid)
7612  {
7613  if(LeadElement > -1)
7614  {
7616  }
7617  if(LeadElement > -1)
7618  {
7620  }
7621  if(MidElement > -1)
7622  {
7624  }
7625  if(MidElement > -1)
7626  {
7628  }
7629  }
7630  else if(Straddle == LeadMidLag)
7631  {
7632  if(LeadElement > -1)
7633  {
7635  }
7636  if(MidElement > -1)
7637  {
7639  }
7640  if(MidElement > -1)
7641  {
7643  }
7644  if(LagElement > -1)
7645  {
7647  }
7648  }
7649  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7650 
7651  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7652  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7653 
7654  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7655 
7656  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7657  if(LeadElement > -1)
7658  // need to include this in case train exiting & no lead element
7659  {
7661  {
7662  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7663  }
7664  }
7665  AValue = sqrt(2 * PowerAtRail / Mass);
7666 
7667  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7668 
7669  // possible RestoreTimetableLocation + Marker, where Marker is
7670  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7671  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7672  // added at beta v0.2e
7673  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7674  // name not allowed to include the '*' character
7675  {
7676  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7677  bool GiveMessagesFalse = false;
7678  bool CheckLocationsExistInRailwayTrue = true;
7679  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7680  {
7681  // otherwise take no action
7682  RestoreTimetableLocation = Location;
7683  }
7684  }
7685  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7686 
7687  StoppedWithoutPower = false;
7688  if(Marker[6] == '1')
7689  {
7690  StoppedWithoutPower = true;
7691  }
7692  Utilities->CallLogPop(1458);
7693 }
7694 
7695 // ---------------------------------------------------------------------------
7696 
7697 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7698 {
7700  {
7701  return(false); // HeadCode
7702 
7703  }
7704  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7705  {
7706  return(false); // RearStartElement
7707 
7708  }
7709  if(!Utilities->CheckFileInt(InFile, 0, 3))
7710  {
7711  return(false); // RearStartExitPos
7712 
7713  }
7714  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7715  {
7716  return(false); // StartSpeed
7717 
7718  }
7719  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7720  {
7721  return(false); // SignallerMaxSpeed
7722 
7723  }
7724  if(!Utilities->CheckFileBool(InFile))
7725  {
7726  return(false); // HoldAtLocationInTTMode
7727 
7728  }
7729  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7730  {
7731  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7732 
7733  }
7734  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7735  {
7736  return(false); // IncrementalMinutes (max 96 x 60)
7737 
7738  }
7739  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7740  {
7741  return(false); // IncrementalDigits
7742 
7743  }
7744  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7745  {
7746  return(false); // Mass
7747 
7748  }
7749  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7750  {
7751  return(false);
7752  }
7753  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7754  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7755  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7756  {
7757  return(false); // FrontElementLength
7758 
7759  }
7760  if(!Utilities->CheckFileDouble(InFile))
7761  {
7762  return(false); // EntrySpeed
7763 
7764  }
7765  if(!Utilities->CheckFileDouble(InFile))
7766  {
7767  return(false); // ExitSpeedHalf
7768 
7769  }
7770  if(!Utilities->CheckFileDouble(InFile))
7771  {
7772  return(false); // ExitSpeedFull
7773 
7774  }
7775  if(!Utilities->CheckFileDouble(InFile))
7776  {
7777  return(false); // TimetableMaxRunningSpeed
7778 
7779  }
7780  if(!Utilities->CheckFileDouble(InFile))
7781  {
7782  return(false); // MaxRunningSpeed
7783 
7784  }
7785  if(!Utilities->CheckFileDouble(InFile))
7786  {
7787  return(false); // MaxExitSpeed
7788 
7789  }
7790  if(!Utilities->CheckFileDouble(InFile))
7791  {
7792  return(false); // MaxBrakeRate
7793 
7794  }
7795  if(!Utilities->CheckFileDouble(InFile))
7796  {
7797  return(false); // BrakeRate
7798 
7799  }
7800  if(!Utilities->CheckFileDouble(InFile))
7801  {
7802  return(false); // PowerAtRail
7803 
7804  }
7805  if(!Utilities->CheckFileBool(InFile))
7806  {
7807  return(false); // FirstHalfMove
7808 
7809  }
7810  if(!Utilities->CheckFileBool(InFile))
7811  {
7812  return(false); // OneLengthAccelDecel
7813 
7814  }
7815  if(!Utilities->CheckFileDouble(InFile))
7816  {
7817  return(false); // double(EntryTime)
7818 
7819  }
7820  if(!Utilities->CheckFileDouble(InFile))
7821  {
7822  return(false); // double(ExitTimeHalf)
7823 
7824  }
7825  if(!Utilities->CheckFileDouble(InFile))
7826  {
7827  return(false); // double(ExitTimeFull)
7828 
7829  }
7830  if(!Utilities->CheckFileDouble(InFile))
7831  {
7832  return(false); // double(ReleaseTime)
7833 
7834  }
7835  if(!Utilities->CheckFileDouble(InFile))
7836  {
7837  return(false); // double(TRSTime)
7838 
7839  }
7840  if(!Utilities->CheckFileDouble(InFile))
7841  {
7842  return(false); // double(LastActionTime)
7843 
7844  }
7845  if(!Utilities->CheckFileBool(InFile))
7846  {
7847  return(false); // CallingOnFlag
7848 
7849  }
7850  if(!Utilities->CheckFileBool(InFile))
7851  {
7852  return(false); // BeingCalledOn
7853 
7854  }
7855  if(!Utilities->CheckFileBool(InFile))
7856  {
7857  return(false); // DepartureTimeSet
7858 
7859  }
7860  if(!Utilities->CheckFileInt(InFile, 0, 2))
7861  {
7862  return(false); // (short)TrainMode
7863 
7864  }
7865  if(!Utilities->CheckFileBool(InFile))
7866  {
7867  return(false); // TimetableFinished
7868 
7869  }
7870  if(!Utilities->CheckFileBool(InFile))
7871  {
7872  return(false); // LastActionDelayFlag
7873 
7874  }
7875  if(!Utilities->CheckFileBool(InFile))
7876  {
7877  return(false); // SignallerRemoved
7878 
7879  }
7880  if(!Utilities->CheckFileBool(InFile))
7881  {
7882  return(false); // TerminatedMessageSent
7883 
7884  }
7885  if(!Utilities->CheckFileBool(InFile))
7886  {
7887  return(false); // Derailed
7888 
7889  }
7890  if(!Utilities->CheckFileBool(InFile))
7891  {
7892  return(false); // DerailPending
7893 
7894  }
7895  if(!Utilities->CheckFileBool(InFile))
7896  {
7897  return(false); // Crashed
7898 
7899  }
7900  if(!Utilities->CheckFileBool(InFile))
7901  {
7902  return(false); // StoppedAtBuffers
7903 
7904  }
7905  if(!Utilities->CheckFileBool(InFile))
7906  {
7907  return(false); // StoppedAtSignal
7908 
7909  }
7910  if(!Utilities->CheckFileBool(InFile))
7911  {
7912  return(false); // StoppedAtLocation
7913 
7914  }
7915  if(!Utilities->CheckFileBool(InFile))
7916  {
7917  return(false); // SignallerStopped
7918 
7919  }
7920  if(!Utilities->CheckFileBool(InFile))
7921  {
7922  return(false); // StoppedAfterSPAD
7923 
7924  }
7925  if(!Utilities->CheckFileBool(InFile))
7926  {
7927  return(false); // StoppedForTrainInFront
7928 
7929  }
7930  if(!Utilities->CheckFileBool(InFile))
7931  {
7932  return(false); // NotInService
7933 
7934  }
7935  if(!Utilities->CheckFileBool(InFile))
7936  {
7937  return(false); // Plotted
7938 
7939  }
7940  if(!Utilities->CheckFileBool(InFile))
7941  {
7942  return(false); // TrainGone
7943 
7944  }
7945  if(!Utilities->CheckFileBool(InFile))
7946  {
7947  return(false); // SPADFlag
7948 
7949  }
7950  if(!Utilities->CheckFileBool(InFile))
7951  {
7952  return(false); // TimeTimeLocArrived
7953 
7954  }
7955  if(!Utilities->CheckFileInt(InFile, 0, 15))
7956  {
7957  return(false); // HOffset[0]
7958 
7959  }
7960  if(!Utilities->CheckFileInt(InFile, 0, 15))
7961  {
7962  return(false); // HOffset[1]
7963 
7964  }
7965  if(!Utilities->CheckFileInt(InFile, 0, 15))
7966  {
7967  return(false); // HOffset[2]
7968 
7969  }
7970  if(!Utilities->CheckFileInt(InFile, 0, 15))
7971  {
7972  return(false); // HOffset[3]
7973 
7974  }
7975  if(!Utilities->CheckFileInt(InFile, 0, 15))
7976  {
7977  return(false); // VOffset[0]
7978 
7979  }
7980  if(!Utilities->CheckFileInt(InFile, 0, 15))
7981  {
7982  return(false); // VOffset[1]
7983 
7984  }
7985  if(!Utilities->CheckFileInt(InFile, 0, 15))
7986  {
7987  return(false); // VOffset[2]
7988 
7989  }
7990  if(!Utilities->CheckFileInt(InFile, 0, 15))
7991  {
7992  return(false); // VOffset[3]
7993 
7994  }
7995  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7996  {
7997  return(false); // PlotElement[0]
7998 
7999  }
8000  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8001  {
8002  return(false); // PlotElement[1]
8003 
8004  }
8005  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8006  {
8007  return(false); // PlotElement[2]
8008 
8009  }
8010  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8011  {
8012  return(false); // PlotElement[3]
8013 
8014  }
8015  if(!Utilities->CheckFileInt(InFile, 0, 3))
8016  {
8017  return(false); // PlotEntryPos[0]
8018 
8019  }
8020  if(!Utilities->CheckFileInt(InFile, 0, 3))
8021  {
8022  return(false); // PlotEntryPos[1]
8023 
8024  }
8025  if(!Utilities->CheckFileInt(InFile, 0, 3))
8026  {
8027  return(false); // PlotEntryPos[2]
8028 
8029  }
8030  if(!Utilities->CheckFileInt(InFile, 0, 3))
8031  {
8032  return(false); // PlotEntryPos[3]
8033 
8034  }
8035  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8036  {
8037  return(false); // TrainCrashedInto
8038 
8039  }
8040  if(!Utilities->CheckFileInt(InFile, 0, 2))
8041  {
8042  return(false); // (short)Straddle
8043 
8044  }
8045  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8046  {
8047  return(false); // NextTrainID
8048 
8049  }
8050  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8051  {
8052  return(false); // TrainID
8053 
8054  }
8055  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8056  {
8057  return(false); // LeadElement
8058 
8059  }
8060  if(!Utilities->CheckFileInt(InFile, 0, 3))
8061  {
8062  return(false); // LeadEntryPos
8063 
8064  }
8065  if(!Utilities->CheckFileInt(InFile, 0, 3))
8066  {
8067  return(false); // LeadExitPos
8068 
8069  }
8070  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8071  {
8072  return(false); // MidElement
8073 
8074  }
8075  if(!Utilities->CheckFileInt(InFile, 0, 3))
8076  {
8077  return(false); // MidEntryPos
8078 
8079  }
8080  if(!Utilities->CheckFileInt(InFile, 0, 3))
8081  {
8082  return(false); // MidExitPos
8083 
8084  }
8085  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8086  {
8087  return(false); // LagElement
8088 
8089  }
8090  if(!Utilities->CheckFileInt(InFile, 0, 3))
8091  {
8092  return(false); // LagEntryPos
8093 
8094  }
8095  if(!Utilities->CheckFileInt(InFile, 0, 3))
8096  {
8097  return(false); // LagExitPos
8098 
8099  }
8100  if(!Utilities->CheckFileInt(InFile, 0, 14))
8101  {
8102  return(false);
8103  }
8104  // Background colour number //14 is failed colour at v2.4.0
8105  if(!Utilities->CheckFileBool(InFile))
8106  {
8107  return(false); // ForwardHeadCode
8108 
8109  }
8110  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8111  {
8112  return(false); // TrainDataEntryValue
8113 
8114  }
8115  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8116  {
8117  return(false); // ActionVectorEntryValue
8118 
8119  }
8121  {
8122  return(false); // End of train marker + possible RestoreTimetableLocation
8123 
8124  }
8125  // and StoppedWithoutPower flag
8126  return(true);
8127 }
8128 
8129 // ---------------------------------------------------------------------------
8130 
8131 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8132 {
8133  // order below reflects significance so earlier shows first, as may have more than one flag set
8134  // only plot flashing trains when Flash is true
8135 
8136 /*
8137  clCrashedBackground (TColor)0x0000FF red
8138  clDerailedBackground (TColor)0x0000FF red
8139  clSPADBackground (TColor)0x00FFFF yellow
8140  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8141  clCallOnBackground (TColor)0xFF33FF light magenta
8142  clSignalStopBackground (TColor)0x00FF66 green
8143  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8144  clStationStopBackground (TColor)0xCCFFCC pale green
8145  clTRSBackground (TColor)0xFFCCFF light pink
8146  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8147  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8148  clSignallerStopped (TColor)0x99CCFF caramel
8149  clNormalBackground (TColor)0xCCCCCC grey
8150 */
8151 
8152  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8153  bool HideFlashingTrain = true;
8154  // hide it when Flash false so it blinks on and off
8155  // if don't hide it it stays displayed all the time
8156  Graphics::TBitmap *SmallTrainBitmap;
8157 
8158  // NB ensure retain same order as zoomed in order so colours correspond
8160  {
8161  TrainController->CrashWarning = true;
8162  SmallTrainBitmap = RailGraphics->smRed;
8163  }
8165  {
8167  SmallTrainBitmap = RailGraphics->smRed;
8168  }
8170  {
8171  TrainController->SPADWarning = true;
8172  SmallTrainBitmap = RailGraphics->smYellow;
8173  }
8175  {
8177  SmallTrainBitmap = RailGraphics->smOrange;
8178  }
8180  {
8182  SmallTrainBitmap = RailGraphics->smMagenta;
8183  }
8185  {
8187  SmallTrainBitmap = RailGraphics->smBrightGreen;
8188  }
8190  {
8192  SmallTrainBitmap = RailGraphics->smCyan;
8193  }
8195  {
8196  SmallTrainBitmap = RailGraphics->smPaleGreen;
8197  HideFlashingTrain = false;
8198  }
8200  {
8201  SmallTrainBitmap = RailGraphics->smCyan;
8202  HideFlashingTrain = false;
8203  }
8205  {
8206  SmallTrainBitmap = RailGraphics->smLightBlue;
8207  HideFlashingTrain = false;
8208  }
8210  {
8211  SmallTrainBitmap = RailGraphics->smCaramel;
8212  HideFlashingTrain = false;
8213  }
8214  else
8215  {
8216  SmallTrainBitmap = RailGraphics->smBlack; // moving
8217  HideFlashingTrain = false;
8218  }
8219  // now plot the new train
8220  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8221  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8222  {
8223  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8224  }
8225  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8226  {
8227  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8228  }
8229  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8230  {
8231  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8232  }
8236  Utilities->CallLogPop(1459);
8237 }
8238 
8239 // ---------------------------------------------------------------------------
8240 
8242 {
8243  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8244  if(!Display->ZoomOutFlag)
8245  {
8246  Utilities->CallLogPop(1304);
8247  return;
8248  }
8249  for(int y = 0; y < 3; y++)
8250  {
8251  if(OldZoomOutElement[y] > -1)
8252  {
8253  bool FoundFlag = false;
8254  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8255  TTrackElement IATElement1, IATElement2;
8256  // default elements to begin with
8257  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8258  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8259  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8260  if(FoundFlag)
8261  {
8262  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8263  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8264  if(IMPair.first != IMPair.second)
8265  {
8266  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8267  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8268  }
8269  }
8270  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8271  }
8272  }
8273  Utilities->CallLogPop(1305);
8274 }
8275 
8276 // ---------------------------------------------------------------------------
8277 
8278 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8279 {
8280  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8281  LocationName = "";
8282  if(!StoppedAtLocation)
8283  {
8284  Utilities->CallLogPop(1398);
8285  return(false);
8286  }
8287  if(LeadElement > -1)
8288  {
8290  }
8291  if((LocationName == "") && (MidElement > -1))
8292  {
8293  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8294  }
8295  if((LocationName == "") && (LagElement > -1))
8296  {
8297  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8298  }
8299  if(LocationName == "")
8300  {
8301  throw Exception("Error - Location name not set in TrainAtLocation");
8302  }
8303  Utilities->CallLogPop(1399);
8304  return(true);
8305 }
8306 
8307 // ---------------------------------------------------------------------------
8308 
8309 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8310 {
8311  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8312  for(int x = 0; x < 4; x++)
8313  {
8314  PlotTrainGraphic(7, x, Disp);
8315  }
8316  Utilities->CallLogPop(647);
8317 }
8318 
8319 // ---------------------------------------------------------------------------
8320 
8321 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8322 {
8323  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8324  for(int x = 0; x < 4; x++)
8325  {
8326  if(PlotElement[x] > -1)
8327  {
8328  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8329  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8330  }
8331  }
8332  Utilities->CallLogPop(1708);
8333 }
8334 
8335 // ---------------------------------------------------------------------------
8336 
8337 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8338 {
8339  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8340  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8341  AnsiString(LinkNumber) + "," + HeadCode);
8342 
8343 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8344  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8345  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8346 */
8347 
8348  // note that MidElement always fully occupied
8349  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8350  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8351  {
8352  Utilities->CallLogPop(2005);
8353  return(true);
8354  }
8355  if(Straddle == LeadMid)
8356  {
8357  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8358  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8359  {
8360  Utilities->CallLogPop(2006);
8361  return(true);
8362  }
8363  }
8364  else if(Straddle == LeadMidLag)
8365  {
8366  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8367  // only interested in LeadEntryPos as train not occupying ExitPos yet
8368  {
8369  Utilities->CallLogPop(2007);
8370  return(true);
8371  }
8372  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8373  // only interested in LagExitPos as train has left EntryPos
8374  {
8375  Utilities->CallLogPop(2008);
8376  return(true);
8377  }
8378  }
8379  Utilities->CallLogPop(2009);
8380  return(false);
8381 }
8382 
8383 // ---------------------------------------------------------------------------
8384 
8385 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
8390 {
8391  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8392  int DistanceToRedSignal = 0;
8393  float TimeToAct = 0;
8394  float MinsEarly = 0; //added at v2.6.1
8395  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8396  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8397 
8398  if(TrainFailed)
8399  {
8400  Utilities->CallLogPop(2147);
8401  return(0); // time to act now
8402  }
8403  if(SignallerStopped)
8404  {
8405  Utilities->CallLogPop(2080);
8406  return(-1);
8407  }
8408  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8409  {
8410  Utilities->CallLogPop(2266);
8411  return(-1);
8412  }
8413  if(!Stopped() || StoppedAtLocation)
8414  {
8415  // calc distance to next red signal but check for continuation exit
8416  if(LeadElement == -1)
8417  // if -1 it's on an end element so no action needed
8418  {
8419  Utilities->CallLogPop(2075);
8420  return(-1);
8421  }
8422  else
8423  {
8424  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8425  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8426  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8427 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8428  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8429  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8430  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8431  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8432  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8433  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8434  before the train has stopped the current station is still recognised as a future stop.
8435  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8436  becomes the signal, and the time to act indication becomes 'NOW'.
8437 */
8438  {
8439  FirstPosToBeMeasured = LeadElement;
8440  FirstEntryPos = LeadEntryPos;
8441  }
8442  float CurrentStopTime; // set to 0 at start of function
8443  float LaterStopTime; // set to 0 at start of function
8444  float RecoverableTime; // set to 0 at start of function
8445  int AvTrackSpeed; // set to zero at start of function
8446  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8447  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8448  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
8449  if(DistanceToRedSignal == -1) // -1 for no action needed
8450  {
8451  Utilities->CallLogPop(2076);
8452  return(-1);
8453  }
8454 /* Have MinsDelayed; pos or neg,
8455  CurrentStopTime; pos or zero
8456  LaterStopTime; pos or zero
8457  RecoverableTime; pos or zero
8458 
8459  & from these calculate TotalStopTime. noting that:
8460  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8461  RecoverableTime always < LaterStopTime or both zero
8462  can't subtract more than RecoverableTime (MinsDelayed > 0)
8463  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8464  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8465  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8466  if running early & stopped at location CurrentStopTime will automatically include the excess
8467 */
8468  float TimeToSubtract, TotalStopTime;
8469  if(MinsDelayed > RecoverableTime)
8470  {
8471  TimeToSubtract = RecoverableTime;
8472  }
8473  else
8474  {
8475  TimeToSubtract = MinsDelayed; // may be negative;
8476 
8477  }
8478  if((AvTrackSpeed > 0) && (DistanceToStationStop <= DistanceToRedSignal) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8479  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8480  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8481  //next station stop
8482  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8483  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8484  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8485  //first find departure time from the next stop
8486  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8487  {
8488  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8489  {
8492  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8493  }
8494  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8495  {
8497  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8498  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8499  {
8500  // must be a departure
8501  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8502  }
8503  }
8504  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8505  {
8506  MinsEarly = 0;
8507  }
8508  if(MinsEarly < 0)
8509  {
8510  MinsEarly = 0;
8511  }
8512  }
8513  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8514  {
8515  if(CurrentStopTime > 0)
8516  {
8517  TotalStopTime = CurrentStopTime + LaterStopTime;
8518  }
8519  // stopped at loc, will depart on time
8520  else
8521  {
8522  TotalStopTime = LaterStopTime - MinsDelayed;
8523  }
8524  // not stopped, will depart on time at first later stop so add the delay
8525  }
8526  else if((MinsEarly > 0) && !Stopped()) //running early
8527  {
8528  TotalStopTime = LaterStopTime + MinsEarly;
8529  }
8530  else // on time or running late
8531  {
8532  if(LaterStopTime == 0)
8533  {
8534  TotalStopTime = CurrentStopTime;
8535  }
8536  // no later stops, if stopped now will depart as soon as possible,
8537  // if not stopped no stop times to add
8538  else
8539  {
8540  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8541  }
8542  }
8543  if(AvTrackSpeed < 30)
8544  {
8545  AvTrackSpeed = 30;
8546  }
8547  int Speed = AvTrackSpeed;
8548  if(AvTrackSpeed > int(MaxRunningSpeed))
8549  {
8550  Speed = int(MaxRunningSpeed);
8551  }
8552  if(TrainMode == Signaller)
8553  {
8554  Speed = SignallerMaxSpeed;
8555  TotalStopTime = 0;
8556  }
8557  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
8558  // accel & decel taken into account in
8559  // CalcDistanceToRedSignalandStopTime
8560  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8561  Utilities->CallLogPop(2079);
8562  return(TimeToAct);
8563  }
8564  }
8565  else // stopped not at location
8566  {
8568  {
8569  TimeToAct = 0.0;
8570  }
8571  // but if stopped at a signal & autosigs route after it then ok
8572  if(StoppedAtSignal)
8573  {
8574  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8575  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8576  int NextExitPos;
8577  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8578  {
8579  if((NextEntryPos == 0) || (NextEntryPos == 2))
8580  // leading entry point
8581  {
8582  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8583  {
8584  NextExitPos = 1;
8585  }
8586  else
8587  {
8588  NextExitPos = 3;
8589  }
8590  }
8591  else
8592  {
8593  NextExitPos = 0; // trailing entry point
8594  }
8595  }
8596  else
8597  {
8598  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8599  }
8600  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8601  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8602  int RouteNumber; // holder for referenced value, not used
8603  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8604  {
8605  TimeToAct = -1;
8606  }
8607  }
8608  Utilities->CallLogPop(2074);
8609  return(TimeToAct);
8610  }
8611 }
8612 
8613 // ---------------------------------------------------------------------------
8614 
8616 {
8617  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8618  if(LeadElement > -1)
8619  {
8621  {
8622  Utilities->CallLogPop(2148);
8623  return(true);
8624  }
8625  }
8626  if(MidElement > -1)
8627  {
8629  {
8630  Utilities->CallLogPop(2149);
8631  return(true);
8632  }
8633  }
8634  if(LagElement > -1)
8635  {
8637  {
8638  Utilities->CallLogPop(2150);
8639  return(true);
8640  }
8641  }
8642  Utilities->CallLogPop(2151);
8643  return(false);
8644 }
8645 
8646 // ---------------------------------------------------------------------------
8647 // TTrainController
8648 // ---------------------------------------------------------------------------
8649 
8651 {
8652  OnTimeArrivals = 0;
8653  LateArrivals = 0;
8654  EarlyArrivals = 0;
8655  OnTimePasses = 0;
8656  LatePasses = 0;
8657  EarlyPasses = 0;
8658  OnTimeDeps = 0;
8659  LateDeps = 0;
8660  MissedStops = 0;
8661  OtherMissedEvents = 0;
8662  UnexpectedExits = 0;
8663  NumFailures = 0;
8664  IncorrectExits = 0;
8665  SPADEvents = 0;
8666  SPADRisks = 0;
8667  CrashedTrains = 0;
8668  Derailments = 0;
8669  TotArrDepPass = 0;
8670  TotLateArrMins = 0;
8671  TotEarlyArrMins = 0;
8672  TotLatePassMins = 0;
8673  TotEarlyPassMins = 0;
8674  TotLateExitMins = 0; //added at v2.9.1
8675  TotEarlyExitMins = 0; //added at v2.9.1
8676  TotLateDepMins = 0;
8677  ExcessLCDownMins = 0;
8678  TTClockTime = 0; // added for v0.6
8680  // added at v1.3.0 to ensure false at start
8681  OpTimeToActUpdateCounter = 0; // new v2.2.0
8682  OpActionPanelVisible = false; // new v2.2.0
8683  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8684  SSHigh = false;
8685  MRSHigh = false;
8686  MRSLow = false;
8687  MassHigh = false;
8688  BFHigh = false;
8689  BFLow = false;
8690  PwrHigh = false;
8691  SigSHigh = false;
8692  SigSLow = false;
8693  randomize();
8694  // to seed rand() & random() with a random number (see UpdateTrain)
8695 }
8696 
8697 // ---------------------------------------------------------------------------
8698 
8700 {
8701  for(unsigned int x = 0; x < TrainVector.size(); x++)
8702  {
8703  TrainVectorAt(32, x).DeleteTrain(4);
8704  }
8705  TrainVector.clear();
8706 }
8707 
8708 // ---------------------------------------------------------------------------
8709 
8710 void TTrainController::LogEvent(AnsiString Str)
8711 {
8712  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8713 
8714  // restrict to last 1000 entries
8715  Utilities->EventLog.push_back(FullStr);
8716  if(Utilities->EventLog.size() > 1000)
8717  {
8718  Utilities->EventLog.pop_front();
8719  }
8720 }
8721 
8722 // ---------------------------------------------------------------------------
8723 
8725 {
8726  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8727  bool ClockState = Utilities->Clock2Stopped;
8728 
8729  Utilities->Clock2Stopped = true;
8730  // new section dealing with Snt & Snt-sh additions
8731  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8732  // clock tick after stops flashing
8734  {
8735  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8736  {
8737  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8738  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8739  TActionEventType EventType = NoEvent;
8740  if(AVEntry0.Command == "Snt")
8741  {
8742  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8743  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8744  int IncrementalMinutes = 0;
8745  int IncrementalDigits = 0;
8746  if(AVEntryLast.FormatType == Repeat)
8747  {
8748  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8749  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8750  }
8751  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8752  {
8753  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8754  }
8755  // see above note
8756 
8757  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8758  {
8759  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8760  if(TTOD.RunningEntry != NotStarted)
8761  {
8762  continue;
8763  }
8764  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8765  {
8766  break; // all the rest will also be greater
8767  }
8768  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8769  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8770  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8771  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8772  {
8773  TTOD.TrainID = TrainVector.back().TrainID;
8774  TTOD.RunningEntry = Running;
8775  }
8776  else if(EventType == FailTrainEntry)
8777  {
8778  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8779  }
8780  }
8781  }
8782  if(AVEntry0.Command == "Snt-sh")
8783  // just start this once, shuttle repeats take care of restarts
8784  {
8785  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8786  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8787  int IncrementalMinutes = 0;
8788  int IncrementalDigits = 0;
8789  if(AVEntryLast.FormatType == Repeat)
8790  {
8791  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8792  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8793  }
8794  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8795  {
8796  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8797  }
8798  // see above note
8799  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8800  if(TTOD.RunningEntry == NotStarted)
8801  {
8802  if(AVEntry0.EventTime <= TTClockTime)
8803  {
8804  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8805  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8806  TDEntry.SignallerSpeed, false, EventType))
8807  // false for SignallerControl
8808  {
8809  TTOD.TrainID = TrainVector.back().TrainID;
8810  TTOD.RunningEntry = Running;
8811  }
8812  else if(EventType == FailTrainEntry)
8813  {
8814  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8815  }
8816  }
8817  }
8818  }
8819  }
8820  }
8821  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8822  // iteration, next cycle will catch up with any other pending updates
8823  if(!TrainVector.empty())
8824  {
8825  TrainAdded = false;
8826  AllRoutes->CallonVector.clear();
8827  // this will be rebuilt during the calls to UpdateTrain
8828  for(unsigned int x = 0; x < TrainVector.size(); x++)
8829  {
8830  TrainVectorAt(33, x).UpdateTrain(0);
8831  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8832  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8833  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8834  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8835  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8836  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8837  {
8838  break;
8839  }
8840  }
8841  // set warning flags
8842  CrashWarning = false;
8843  DerailWarning = false;
8844  SPADWarning = false;
8845  CallOnWarning = false;
8846  SignalStopWarning = false;
8847  BufferAttentionWarning = false;
8848  TrainFailedWarning = false;
8849  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8850  {
8851  TTrain &Train = TrainVectorAt(34, x);
8852  if(Train.Crashed)
8853  // can't use background colours for crashed & derailed because same colour
8854  {
8855  CrashWarning = true;
8856  }
8857  else if(Train.Derailed)
8858  // can't use background colours for crashed & derailed because same colour
8859  {
8860  DerailWarning = true;
8861  }
8862  else if(Train.BackgroundColour == clSPADBackground)
8863  // use colour as that changes as soon as passes signal
8864  {
8865  SPADWarning = true;
8866  }
8867  else if(Train.BackgroundColour == clTrainFailedBackground)
8868  {
8869  TrainFailedWarning = true;
8870  }
8871  else if(Train.BackgroundColour == clCallOnBackground)
8872  // use colour as also stopped at signal
8873  {
8874  CallOnWarning = true;
8875  }
8876  else if(Train.BackgroundColour == clSignalStopBackground)
8877  // use colour to distinguish from call-on
8878  {
8879  SignalStopWarning = true;
8880  }
8881  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8882  // use colour to distinguish from ordinary buffer stop
8883  {
8884  BufferAttentionWarning = true;
8885  }
8886  if(Train.HasTrainGone())
8887  {
8888  AnsiString Loc = "";
8889  bool ElementFound = false;
8890  TTrackElement TE;
8891  if(Train.LagElement > -1)
8892  {
8893  TE = Track->TrackElementAt(531, Train.LagElement);
8894  ElementFound = true;
8895  }
8896  else if(Train.MidElement > -1)
8897  {
8898  TE = Track->TrackElementAt(779, Train.MidElement);
8899  ElementFound = true;
8900  }
8901  else if(Train.LeadElement > -1)
8902  {
8903  TE = Track->TrackElementAt(780, Train.LeadElement);
8904  ElementFound = true;
8905  }
8906  if(ElementFound)
8907  {
8908  if(TE.ActiveTrackElementName != "")
8909  {
8910  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8911  }
8912  else
8913  {
8914  Loc = "track element " + TE.ElementID;
8915  }
8916  }
8917  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8918  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8919  // need above first because may also have ActionVectorEntryPtr == "Fer"
8920  {
8921  Train.UnplotTrain(9);
8922  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8924  {
8927  }
8928  // end of addition
8929  AllRoutes->RebuildRailwayFlag = true;
8930  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8931  // correctly after a crash
8932  }
8933  else if(AVEntryPtr->Command == "Fer")
8934  {
8935  bool CorrectExit = false;
8936  if(!AVEntryPtr->ExitList.empty())
8937  {
8938  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8939  {
8940  if(*ELIT == Train.LagElement)
8941  {
8942  CorrectExit = true;
8943  }
8944  }
8945  }
8946  if(CorrectExit)
8947  {
8948  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8949  }
8950  else
8951  {
8952  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8953  }
8954  }
8955  else
8956  {
8957  if(!AVEntryPtr->SignallerControl)
8958  {
8959  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8960  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8961  // -2 is marker for send messages for all remaining actions except Fer if present
8962  }
8963  else
8964  {
8965  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8966  }
8967  }
8968  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8969  Train.DeleteTrain(1);
8970  TrainVector.erase(TrainVector.begin() + x);
8971  ReplotTrains(1, Display);
8972  // to reset ElementIDs for remaining trains when have removed a train
8973  }
8974  }
8975  }
8976  else
8977  {
8978  // reset all flags in case last train removed with flag set
8979  CrashWarning = false;
8980  DerailWarning = false;
8981  SPADWarning = false;
8982  CallOnWarning = false;
8983  SignalStopWarning = false;
8984  BufferAttentionWarning = false;
8985  TrainFailedWarning = false;
8986  }
8987  // update OpTimeToActMultimap
8989  {
8991  // clears entries then adds values for running trains then for continuation entries
8992  }
8993  Utilities->Clock2Stopped = ClockState;
8994  Utilities->CallLogPop(723);
8995 }
8996 
8997 // ---------------------------------------------------------------------------
8999 {
9000  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9001  if(!TrainVector.empty())
9002  {
9003  for(int x = TrainVector.size() - 1; x >= 0; x--)
9004  {
9005  TrainVectorAt(50, x).DeleteTrain(2);
9006  }
9007  TrainVector.clear();
9008  }
9009  if(!TrainDataVector.empty())
9010  {
9011  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9012  {
9013  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9014  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9015  {
9016  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9017  TOD.RunningEntry = NotStarted;
9018  TOD.TrainID = -1;
9019  TOD.EventReported = NoEvent;
9020  }
9021  }
9022  }
9023  Display->GetOutputLog1()->Caption = "";
9024  Display->GetOutputLog2()->Caption = "";
9025  Display->GetOutputLog3()->Caption = "";
9026  Display->GetOutputLog4()->Caption = "";
9027  Display->GetOutputLog5()->Caption = "";
9028  Display->GetOutputLog6()->Caption = "";
9029  Display->GetOutputLog7()->Caption = "";
9030  Display->GetOutputLog8()->Caption = "";
9031  Display->GetOutputLog9()->Caption = "";
9032  Display->GetOutputLog10()->Caption = "";
9033  Utilities->CallLogPop(1352);
9034 }
9035 
9036 // ---------------------------------------------------------------------------
9037 
9039 {
9040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9041  if(!TrainVector.empty())
9042  {
9043  for(unsigned int x = 0; x < TrainVector.size(); x++)
9044  {
9045  TrainVectorAt(51, x).PlotTrain(4, Disp);
9046  }
9047  }
9048  Utilities->CallLogPop(724);
9049 }
9050 
9051 // ---------------------------------------------------------------------------
9052 
9053 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9054 {
9055  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9056  if(!TrainVector.empty())
9057  {
9058  for(unsigned int x = 0; x < TrainVector.size(); x++)
9059  {
9060  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9061  }
9062  }
9063  Utilities->CallLogPop(1707);
9064 }
9065 
9066 // ---------------------------------------------------------------------------
9067 
9069 {
9070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9071  if(!TrainVector.empty())
9072  {
9073  for(unsigned int x = 0; x < TrainVector.size(); x++)
9074  {
9075  TrainVectorAt(52, x).UnplotTrain(10);
9076  }
9077  }
9079  Utilities->CallLogPop(725);
9080 }
9081 
9082 // ---------------------------------------------------------------------------
9083 
9084 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9085  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9086  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9087 {
9088  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9089  "," + AnsiString(Mass) + "," + ModeStr);
9090  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9091  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9092 
9093  int RearExitPos = -1;
9094 
9095  for(int x = 0; x < 4; x++)
9096  {
9097  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9098  {
9099  RearExitPos = x;
9100  }
9101  }
9102  if(RearExitPos == -1)
9103  {
9104  throw Exception("Error, RearExit == -1 in AddTrain");
9105  }
9106  bool ReportFlag = true;
9107 
9108  // used to stop repeated messages from CheckStartAllowable when split failed
9109  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9110  {
9111  ReportFlag = false;
9112  }
9113  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9114  {
9115  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9116  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9117  Utilities->CallLogPop(938);
9118  return(false);
9119  }
9120  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9121  TTrainMode TrainMode = NoMode;
9122 
9123  if(ModeStr == "Timetable")
9124  {
9125  TrainMode = Timetable;
9126  }
9127  // all else gives 'None', 'Signaller' set within program
9128 
9129  if(MaxRunningSpeed < 10)
9130  {
9131  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9132  }
9133  if(SignallerSpeed < 10)
9134  {
9135  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9136  }
9137  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9138  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9139 
9140  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9141  // initialise here rather than in TTrain constructor as create trains
9142  // with Null TrainDataEntryPtr when loading session trains
9143  if(SignallerControl)
9144  {
9145  NewTrain->TimetableFinished = true;
9146  NewTrain->SignallerStoppingFlag = false;
9147  NewTrain->TrainMode = Signaller;
9148  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9149  {
9150  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9151  }
9153  }
9154  // deal with starting conditions:-
9155  // unlocated Snt: just report entry & advance pointer
9156  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9157  // Sns doesn't need a new train
9158  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9159  // covers all above located starts
9160  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9161  // wouldn't have accepted the timetable
9162  {
9163  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9164  // StoppedAtBuffers is set in UpdateTrain()
9165  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9166  // buffer end must be ahead of train or would have failed start position check
9167  {
9168  NewTrain->StoppedAtLocation = true;
9169  NewTrain->PlotStartPosition(0);
9171  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9172  NewTrain->ActionVectorEntryPtr->Warning);
9173  if(!SignallerControl) // don't advance if SignalControlEntry
9174  {
9175  NewTrain->ActionVectorEntryPtr++;
9176  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9177  }
9178  NewTrain->LastActionTime = TTClockTime;
9179  }
9180  // else a through station stop
9181  else
9182  {
9183  NewTrain->StoppedAtLocation = true;
9184  NewTrain->PlotStartPosition(10);
9186  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9187  NewTrain->ActionVectorEntryPtr->Warning);
9188  if(!SignallerControl) // don't advance if SignalControlEntry
9189  {
9190  NewTrain->ActionVectorEntryPtr++;
9191  }
9192  NewTrain->LastActionTime = TTClockTime;
9193  }
9194  }
9195  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9196  {
9197  NewTrain->PlotStartPosition(11);
9198  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9199  AnsiString Loc = "";
9200  if(TE.ActiveTrackElementName != "")
9201  {
9202  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9203  }
9204  else
9205  {
9206  Loc = "track element " + TE.ElementID;
9207  }
9208  if(TE.TrackType == Continuation)
9209  {
9210  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9211  }
9212  else
9213  {
9214  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9215  }
9216  if(!SignallerControl) // don't advance if SignalControlEntry
9217  {
9218  NewTrain->ActionVectorEntryPtr++;
9219  }
9220  NewTrain->LastActionTime = TTClockTime;
9221  // no need to set LastActionTime for an unlocated entry
9222  }
9223  // cancel a wrong-direction route if either element of train starts on one
9224  if(NewTrain->LeadElement > -1)
9225  {
9226  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9227  }
9228  if(NewTrain->MidElement > -1)
9229  {
9230  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9231  }
9232  // set signals for a right-direction autosigs route for either element of train on one
9233  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9234  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9235  int RouteNumber = -1;
9236  bool SignalsSet = false;
9237 
9238  if(NewTrain->LeadElement > -1)
9239  {
9240  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9241  {
9242  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9243  int RouteStartPosition;
9244  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9245  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9246  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9247  if(FirstPair.first == RouteNumber)
9248  {
9249  RouteStartPosition = FirstPair.second;
9250  }
9251  else if(SecondPair.first == RouteNumber)
9252  {
9253  RouteStartPosition = SecondPair.second;
9254  }
9255  else
9256  {
9257  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9258  }
9259  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9260  SignalsSet = true;
9261  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9262  }
9263  else if(RouteNumber > -1) // non-autosigsroute
9264  {
9265  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9266  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9267  int FirstELinkPos = TempPDE.GetELinkPos();
9268  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9269  {
9270  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9271  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9272  }
9273  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9274  {
9275  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9276  // remove the last element under LeadElement
9277  }
9278  AllRoutes->RebuildRailwayFlag = true;
9279  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9280  // now deal with a rear linked autosigs route
9281  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9282  {
9283  int LinkedRouteNumber = -1;
9284  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9285  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9286  {
9287  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9288  // this is ok as here we are setting signals from the start of the route
9289  }
9290  }
9291  SignalsSet = true;
9292  }
9293  }
9294  if(NewTrain->MidElement > -1)
9295  // if entering at a continuation MidElement == -1
9296  {
9297  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9298  if(!SignalsSet)
9299  {
9300  RouteNumber = -1;
9301  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9302  {
9303  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9304  int RouteStartPosition;
9305  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9306  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9307  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9308  if(FirstPair.first == RouteNumber)
9309  {
9310  RouteStartPosition = FirstPair.second;
9311  }
9312  else if(SecondPair.first == RouteNumber)
9313  {
9314  RouteStartPosition = SecondPair.second;
9315  }
9316  else
9317  {
9318  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9319  }
9320  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9321  SignalsSet = true;
9322  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9323  }
9324  else if(RouteNumber > -1) // non-autosigsroute
9325  {
9326  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9327  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9328  int FirstELinkPos = TempPDE.GetELinkPos();
9329  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9330  {
9331  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9332  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9333  }
9334  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9335  {
9336  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9337  // remove the last element under LeadElement
9338  }
9339  AllRoutes->RebuildRailwayFlag = true;
9340  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9341  // now deal with a rear linked autosigs route
9342  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9343  {
9344  int LinkedRouteNumber = -1;
9345  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9346  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9347  {
9348  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9349  // this is ok as now we are setting signals from the start of the route
9350  }
9351  }
9352  }
9353  }
9354  }
9355  TrainVector.push_back(*NewTrain);
9356  Utilities->CallLogPop(731);
9357  return(true);
9358 }
9359 
9360 // ---------------------------------------------------------------------------
9361 
9362 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9363 {
9364  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9365  AnsiString(TrackVectorNumber));
9366  int VecPos = -1;
9367 
9368  for(unsigned int x = 0; x < TrainVector.size(); x++)
9369  {
9370  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9371  {
9372  VecPos = x;
9373  }
9374  }
9375  if(VecPos == -1)
9376  {
9377  throw Exception("Error, VecPos not set in EntryPos");
9378  }
9379  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9380  {
9381  Utilities->CallLogPop(734);
9382  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9383  }
9384  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9385  {
9386  Utilities->CallLogPop(735);
9387  return(TrainVectorAt(5, VecPos).MidEntryPos);
9388  }
9389  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9390  {
9391  Utilities->CallLogPop(736);
9392  return(TrainVectorAt(7, VecPos).LagEntryPos);
9393  }
9394  Utilities->CallLogPop(737);
9395  return(-1);
9396 }
9397 
9398 // ---------------------------------------------------------------------------
9399 
9401 {
9402  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9403  for(unsigned int x = 0; x < TrainVector.size(); x++)
9404  {
9405  if(TrainVectorAt(53, x).TrainID == TrainID)
9406  {
9407  Utilities->CallLogPop(738);
9408  return(TrainVectorAt(54, x));
9409  }
9410  }
9411  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9412 }
9413 
9414 // ---------------------------------------------------------------------------
9415 
9416 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9417 // return true if find the train (added at v2.4.0 as can select a removed train
9418 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9419 {
9420  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9421  for(unsigned int x = 0; x < TrainVector.size(); x++)
9422  {
9423  if(TrainVectorAt(69, x).TrainID == TrainID)
9424  {
9425  Utilities->CallLogPop(2152);
9426  return(true);
9427  }
9428  }
9429  Utilities->CallLogPop(2153);
9430  return(false);
9431 }
9432 
9433 // ---------------------------------------------------------------------------
9434 
9435 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9436 {
9437  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9438  Utilities->Format96HHMMSS(Time));
9439  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9440 
9441  Utilities->CallLogPop(2061);
9442  return(RepeatTime);
9443 }
9444 
9445 // ---------------------------------------------------------------------------
9446 
9447 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9448 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9449 {
9450  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9451  AnsiString RetStr = "", PartStr = "";
9452  int Count = 0;
9453  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9454 
9455  Ptr--; // because incremented at start of loop
9456  do
9457  {
9458  Ptr++;
9459  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9460  {
9461  continue; // move past the starting entry
9462  }
9463  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9464  {
9465  break;
9466  }
9467  if(Ptr->SignallerControl)
9468  {
9469  RetStr = "Train under signaller control";
9470  break;
9471  }
9472  if(Ptr->FormatType == TimeTimeLoc)
9473  {
9474  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9475  {
9476  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9477  }
9478  else
9479  {
9480  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9481  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9482  Count++; // because there are 2 entries
9483  }
9484  }
9485  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9486  {
9487  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9488  }
9489  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9490  {
9491  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9492  }
9493  else if(Ptr->FormatType == PassTime) // new
9494  {
9495  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9496  }
9497  else if(Ptr->Command == "Fns")
9498  {
9499  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9500  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9501  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9502  }
9503  else if(Ptr->Command == "F-nshs")
9504  {
9505  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9506  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9507  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9508  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9509  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9510  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9511  }
9512 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9513  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9514  {
9515  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9516  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9517  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9518  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9519  }
9520  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9521  {
9522  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9523  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9524  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9525  }
9526  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9527  {
9528  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9529  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9530  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9531  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9532  }
9533  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9534  {
9535  PartStr = "Terminate at " + Ptr->LocationName;
9536  }
9537  else if(Ptr->Command == "Frh")
9538  {
9539  PartStr = "Terminate at " + Ptr->LocationName;
9540  }
9541  else if(Ptr->Command == "Fer")
9542  {
9543  AnsiString AllowedExits;
9544  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9545  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9546  }
9547  else if(Ptr->Command == "Fjo")
9548  {
9549  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9550  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9551  }
9552  else if(Ptr->Command == "jbo")
9553  {
9554  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9555  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9556  }
9557  else if(Ptr->Command == "fsp")
9558  {
9559  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9560  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9561  }
9562  else if(Ptr->Command == "rsp")
9563  {
9564  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9565  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9566  }
9567  else if(Ptr->Command == "cdt")
9568  {
9569  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9570  }
9571  if(RetStr != "")
9572  {
9573  RetStr = RetStr + '\n' + PartStr;
9574  }
9575  else
9576  {
9577  RetStr = PartStr;
9578  }
9579  Count++;
9580  }
9581  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9582  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9583  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9584  // forward as anyone should wish to see without looking at the full timetable
9585  Utilities->CallLogPop(2072);
9586  return(RetStr);
9587 }
9588 
9589 // ---------------------------------------------------------------------------
9590 
9591 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9592 {
9593  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9594  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9595  AnsiString DepTime = "", EventTime = "";
9596  bool CDTFlag = false; //reports if train changes direction before departs
9597  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9598  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9599  {
9600  if(AVI->Command == "cdt")
9601  {
9602  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9603  continue;
9604  }
9605  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9606  {
9607  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9608  RetStr += "\nNew service splits at " + EventTime;
9609  Utilities->CallLogPop(2237);
9610  return(RetStr);
9611  }
9612  if(AVI->Command == "jbo")
9613  {
9614  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9615  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9616  Utilities->CallLogPop(2238);
9617  return(RetStr);
9618  }
9619  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9620  {
9621  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9622  if(CDTFlag)
9623  {
9624  RetStr += "\nNew service changes direction then departs at " + DepTime;
9625  }
9626  else
9627  {
9628  RetStr += "\nNew service departs at " + DepTime;
9629  }
9630  Utilities->CallLogPop(2239);
9631  return(RetStr);
9632  }
9633  }
9634  Utilities->CallLogPop(2223);
9635  return(RetStr);
9636 }
9637 
9638 // ---------------------------------------------------------------------------
9639 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9640 /*
9641  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9642  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9643  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9644 
9645  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9646  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9647  user wishes
9648 
9649  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9650  descriptive text or anything user wishes
9651  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9652  be ignored) is taken as the timetable start time.
9653  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9654  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9655  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9656  within the timetable if required.
9657  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9658  services)
9659  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9660  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9661 
9662  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9663  text timetable file easier
9664 
9665  form:-
9666  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9667  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9668  then multiple entries, separated by commas, of the form:-
9669 
9670  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9671  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9672  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9673 
9674  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9675  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9676  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9677 
9678  HH:MM;Command (cdt) }TimeCmd }
9679  HH:MM;Location (arr & dep) }TimeLoc }
9680  HH:MM;HH:MM;Location }TimeTimeLoc }
9681  HH:MM;pas;Location }PassTime }
9682  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9683  HH:MM;Fer;set of allowable IDs }ExitRailway }
9684  Command (Frh only) }FinRemHere }
9685 
9686  R;mm;dd;nn. Repeat Repeat entry
9687 
9688  Formats:
9689 
9690  Command only: Frh
9691  Time;Command: cdt
9692  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9693  Time;Command;2 Element IDs: Snt
9694  Time;Comand;n Element IDs: Fer
9695  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9696  Time;Command;2 Element IDs;Headcode Snt-sh
9697  Time;Command;Location pas
9698  Time;Location Arr Dep
9699  Time;Time;Location Arr & dep together
9700 
9701  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9702 
9703  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9704  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9705  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9706 
9707  4 2x Linked entries, all shuttles:
9708 
9709  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9710  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9711  -> Remain Here (at finish location after all repeats)
9712  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9713  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9714 
9715  Allowable successors:-
9716 
9717  Successor state Type
9718 
9719  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9720  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9721  Sfs AtLoc )
9722  Sns AtLoc ) Start
9723  Sns-fsh AtLoc )
9724  Snt-sh AtLoc )
9725  Sns-sh AtLoc )
9726 
9727  pas Moving )
9728  jbo AtLoc )
9729  fsp AtLoc )
9730  rsp AtLoc ) Intermediate
9731  cdt AtLoc )
9732  TimeLoc arr Moving (bef) )
9733  TimeLoc dep AtLoc (bef) )
9734  TimeTimeLoc Moving )
9735 
9736  Fns Repeat/Nothing)
9737  Fjo Repeat/Nothing)
9738  Frh Repeat/Nothing)
9739  Fer Repeat/Nothing) Finish
9740  Frh-sh Repeat )
9741  Fns-sh Repeat )
9742  F-nshs Nothing )
9743 
9744  Descriptions:
9745  Snt New train
9746  Sfs New service from split
9747  Sns New service from another service
9748  Sns-fsh New non-repeating service from a shuttle service
9749  Snt-sh New shuttle train at a timetabled stop
9750  Sns-sh New shuttle service from a feeder service
9751 
9752  pas Pass
9753  jbo Be joined by another train
9754  fsp Front split
9755  rsp Rear split
9756  cdt Change direction of train
9757  TimeLoc arr Arrival
9758  TimeLoc dep Departure
9759  TimeTimeLoc Arrival and departure
9760 
9761  Fns Finish & form a new service
9762  Fjo Finish & join another train
9763  Frh Finish & remain here
9764  Fer Finish & exit railway
9765  Frh-sh Finish & repeat shuttle, finally remain here
9766  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9767  F-nshs Finish & form a shuttle feeder service
9768 */
9769 
9770 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9771 {
9772  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9773  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9774  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9775  // new for v0.2b
9776  // compile ActiveTrackElementNameMap
9777  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9778 
9780  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9781  {
9782  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
9784  == Track->ContinuationNameMap.end())
9785  {
9786  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9787  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
9788  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9789  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9790  }
9791  }
9793  // end of new section
9794  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9795 
9796  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9797  if(TTBLFile.is_open())
9798  {
9799  char *TrainTimetableString = new char[10000];
9800  // enough for over 200 stations, should be adequate!
9801  bool EndOfFile = false;
9802  int Count = 0;
9803  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9804  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9805  // delimiter is '\0' as it's an AnsiString
9806  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9807  // file empty - stores a null in 1st position if doesn't load any characters
9808  {
9809  // may still have eof even if read a line (no CRLF at end), and
9810  // if so need to process it
9811  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9812  TTBLFile.close();
9813  delete[] TrainTimetableString;
9814  Utilities->CallLogPop(1611);
9815  return(false);
9816  }
9817  AnsiString OneLine(TrainTimetableString);
9818  bool FinalCallFalse = false;
9819  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9820  // get rid of lines before the start time
9821  {
9822  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9823  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9824  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9825  // stores a null in 1st position if doesn't load any characters
9826  {
9827  // may still have eof even if read a line (no CRLF at end), and
9828  // if so need to process it
9829  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9830  TTBLFile.close();
9831  delete[] TrainTimetableString;
9832  Utilities->CallLogPop(772);
9833  return(false);
9834  }
9835  OneLine = AnsiString(TrainTimetableString);
9836  }
9837  // here when have accepted the start time
9838  Count++; // increment past the start time
9839  while(!EndOfFile)
9840  {
9841  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9842  // get next line after start time
9843  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9844  // stores a null in 1st position if doesn't load any characters
9845  {
9846  // may still have eof even if read a line (no CRLF at end), and
9847  // if so need to process it
9848  EndOfFile = true;
9849  OneLine = "";
9850  }
9851  else
9852  {
9853  OneLine = AnsiString(TrainTimetableString);
9854  }
9855  if(OneLine.Length() > 9999)
9856  {
9857  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9858  TTBLFile.close();
9859  delete[] TrainTimetableString;
9860  Utilities->CallLogPop(789);
9861  return(false);
9862  }
9863  bool FinalCallFalse = false;
9864  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9865  // false for FinalCall - just checking at this stage
9866  {
9867  TTBLFile.close();
9868  delete[] TrainTimetableString;
9869  Utilities->CallLogPop(770);
9870  return(false);
9871  }
9872  if(EndOfFile && (Count < 2))
9873  // Timetable must contain at least two relevant lines, one for start time and at least one train
9874  {
9875  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9876  TTBLFile.close();
9877  delete[] TrainTimetableString;
9878  Utilities->CallLogPop(771);
9879  return(false);
9880  }
9881  Count++;
9882  }
9883  delete[] TrainTimetableString;
9884  TTBLFile.close();
9885  } // if(TTBLFile.is_open())
9886  else
9887  {
9888  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9889  Utilities->CallLogPop(2154);
9890  return(false);
9891  }
9892  Utilities->CallLogPop(753);
9893  return(true);
9894 }
9895 
9896 // ---------------------------------------------------------------------------
9897 
9898 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9899  bool CheckLocationsExistInRailway) // return true for success
9900 
9901 /* Format:
9902  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9903  descriptive text or anything user wishes
9904  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9905  be ignored) is taken as the timetable start time.
9906  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9907  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9908  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9909  within the timetable if required.
9910  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9911  services)
9912  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9913  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9914 
9915  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9916  text timetable file easier
9917 
9918  form:-
9919  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9920  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9921  then multiple entries, separated by commas, of the form:-
9922 
9923  Format FormatType
9924  [W]HH:MM;Command (cdt) }TimeCmd }
9925  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9926  [W]HH:MM;pas;Location }PassTime }
9927  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9928  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9929  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9930  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9931  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9932  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9933  [W]HH:MM;Fns-sh;Details }FSHNewService }
9934  [W]HH:MM;Location (arr & dep) }TimeLoc }
9935  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9936  Command (Frh only) }FinRemHere }
9937 
9938  R;mm;dd;nn. Repeat Repeat entry
9939 
9940  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9941  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9942  at location; or (c) departure time if train already at location (including train started at location either as a new
9943  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9944  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9945  minutes, incremental train headcode last 2 digits, and number of repeats.
9946 
9947  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9948  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9949  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9950  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9951  (it's for a shuttle train to return to depot at end of services)
9952 
9953  Command/Location & details are as follows:-
9954 
9955  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9956  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9957  2E44 in its Sfs entry. All these are checked.
9958  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9959 
9960  Start commands:-
9961  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9962  with loc as a start entry can't have a location as details)
9963  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9964  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9965  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9966  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9967  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9968  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9969  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9970  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9971  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9972  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9973  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9974 
9975  Intermediate commands:-
9976  Time - Location (TimeLoc), can be arrival or departure depending on context
9977  Time Time location (TimeTimeLoc), arrival and departure
9978  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9979  pas (PassTime), Time;pas;Location
9980  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9981  joining train's finish details must correspond or the file check will fail
9982  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9983  new train - that train's starting information must correspond)
9984  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9985  new train - that train's starting information must correspond)
9986  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9987 
9988  Finish commands:-
9989  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9990  creation)
9991  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9992  shuttle headcode (no train creation)
9993  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9994  may have to wait for it), details = new headcode (delete train)
9995  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9996  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9997  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9998  here
9999  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10000  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10001 
10002  Repeat:-
10003  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10004  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10005 
10006  Checks carried out with error messages in this function:-
10007  At least one comma in a service line (it's based on a .csv file)
10008  No entries following train information;
10009  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10010  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10011  First entry not a start entry;
10012  Train information incomplete before a start entry;
10013  Entry follows a finish entry but doesn't begin with 'R';
10014  SplitEntry returns false in a finish entry - message repeats the entry for information;
10015  Last action entry isn't a finish entry.
10016 
10017  Function returns false with no message if:-
10018  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10019  time is found at all then an error message is given in the calling function);
10020  SplitTrainInfo returns false (message given in called function);
10021  SplitRepeat returns false (message given in called function).
10022 */{
10023  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10024  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10025  TTrainDataEntry TempTrainDataEntry;
10026 
10027  EndOfFile = false;
10028  StripSpaces(0, OneLine);
10029  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10030  // semicolons within the line
10031  ServiceReference = "";
10032  if(OneLine != "")
10033  {
10034  if(OneLine[1] != '*')
10035  {
10036  int SCPos = OneLine.Pos(';');
10037  if(SCPos == 0)
10038  {
10039  ServiceReference = OneLine.SubString(1, 8);
10040  }
10041  else
10042  {
10043  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10044  }
10045  }
10046  }
10047  bool AllCommas = true;
10048 
10049  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10050  {
10051  if(OneLine[x] != ',')
10052  {
10053  AllCommas = false;
10054  }
10055  }
10056  if(AllCommas || (OneLine == ""))
10057  {
10058  if(Count > 0)
10059  {
10060  EndOfFile = true;
10061  // returns true for a blank line - treated as end of file
10062  Utilities->CallLogPop(1018);
10063  return(true);
10064  }
10065  else // count == 0 so not yet found a start time, no message to be given
10066  {
10067  Utilities->CallLogPop(754);
10068  return(false);
10069  }
10070  }
10071  AnsiString First = "", Second = "", Third = "", Fourth = "";
10072  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10073  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10074  TDateTime StartTime(0);
10075  TExitList ExitList;
10076  bool Warning = false;
10077 
10078  if(Count == 0) // no start time found yet
10079  {
10080 /* dropped at v0.6b
10081  AnyHeadCodeValid = false;
10082  if(OneLine.SubString(6,5) == ";0000")
10083  {
10084  AnyHeadCodeValid = true;
10085  }
10086 */
10087  if(!CheckTimeValidity(0, OneLine, StartTime))
10088  {
10089  // no message is given for an invalid time as it's assumed to be an irrelevant line
10090  // if no start time is found at all then an error message is given in the calling function
10091  // AnyHeadCodeValid = false;
10092  Utilities->CallLogPop(755);
10093  return(false);
10094  }
10095  if(FinalCall) // here if start time valid
10096  {
10097  TTClockTime = StartTime;
10098  TimetableStartTime = StartTime;
10099  }
10100  }
10101  else
10102  {
10103  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10104  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10105  double MaxBrakeRate = 0;
10106  double PowerAtRail = 0;
10107  int SignallerSpeed = 0;
10108  if(OneLine[1] == '*')
10109  {
10110  Utilities->CallLogPop(1581);
10111  return(true);
10112  // ignore any line beginning with '*' but return true as there is no error
10113  }
10114  int Pos = OneLine.Pos(',');
10115  if(Pos == 0)
10116  {
10117  int SubStringLength = 20;
10118  if(OneLine.Length() < 20)
10119  {
10120  SubStringLength = OneLine.Length();
10121  }
10122  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10123  Utilities->CallLogPop(766);
10124  return(false);
10125  }
10126  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10127  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10128  GiveMessages)) // error messages given in SplitTrainInfo
10129  {
10130  Utilities->CallLogPop(773);
10131  return(false);
10132  }
10133  if(FinalCall)
10134  {
10135  // store Train info - conversions done in SplitTrainInfo
10136  // only headcode mandatory for continuing services
10137  TempTrainDataEntry.HeadCode = HeadCode;
10138  TempTrainDataEntry.ServiceReference = HeadCode;
10139  TempTrainDataEntry.Description = Description;
10140  TempTrainDataEntry.StartSpeed = StartSpeed;
10141  TempTrainDataEntry.Mass = Mass;
10142  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10143  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10144  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10145  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10146  TTrainOperatingData TempTrainOperatingData;
10147  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10148  }
10149  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10150  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10151  // so strip them off
10152  while(NewRemainder[NewRemainder.Length()] == ',')
10153  {
10154  if(NewRemainder.Length() > 1)
10155  {
10156  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10157  }
10158  else
10159  {
10160  NewRemainder = "";
10161  break;
10162  }
10163  }
10164  // check if zero length & fail if so
10165  if(NewRemainder == "")
10166  {
10167  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10168  Utilities->CallLogPop(769);
10169  return(false);
10170  }
10171  // now have one more entry than there are commas
10172  int CommaCount = 0;
10173  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10174  {
10175  if(NewRemainder[x] == ',')
10176  {
10177  CommaCount++;
10178  }
10179  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10180  if(CommaCount == 0)
10181  {
10182  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10183  {
10184  int SubStringLength = 20;
10185  if(OneLine.Length() < 20)
10186  {
10187  SubStringLength = OneLine.Length();
10188  }
10189  TimetableMessage(GiveMessages,
10190  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10191  OneLine.SubString(1, SubStringLength) + "'....");
10192  Utilities->CallLogPop(783);
10193  return(false);
10194  }
10195  }
10196  AnsiString OneEntry = "";
10197  TTimetableFormatType FormatType;
10198  TTimetableSequenceType SequenceType;
10199  TTimetableLocationType LocationType;
10200  TTimetableShuttleLinkType ShuttleLinkType;
10201  bool FinishFlag = false;
10202  for(int x = 0; x < CommaCount + 1; x++)
10203  {
10204  if((CommaCount == 0) || (x < CommaCount))
10205  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10206  // in which case there's a comma & finish element or repeat still to come this entry could
10207  // be a finish but can't be a repeat
10208  {
10209  if(CommaCount == 0)
10210  {
10211  OneEntry = NewRemainder;
10212  NewRemainder = "";
10213  }
10214  else
10215  {
10216  Pos = NewRemainder.Pos(',');
10217  OneEntry = NewRemainder.SubString(1, Pos - 1);
10218  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10219  }
10220  First = "";
10221  Second = "";
10222  Third = "";
10223  Fourth = "";
10224  RearStartOrRepeatMins = 0;
10225  FrontStartOrRepeatDigits = 0;
10226  NumberOfRepeats = 0;
10227  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10228  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10229  {
10230  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10231  Utilities->CallLogPop(756);
10232  return(false);
10233  }
10234  // check if warning for Frh or Fjo & reject
10235  if(Warning && (Second == "Frh"))
10236  {
10237  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10238  Utilities->CallLogPop(1793);
10239  return(false);
10240  }
10241  if(Warning && (Second == "Fjo"))
10242  {
10243  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10244  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10245  Utilities->CallLogPop(1794);
10246  return(false);
10247  }
10248  if(x == 0) // should be start event
10249  {
10250  if(SequenceType != Start)
10251  {
10252  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10253  Utilities->CallLogPop(784);
10254  return(false);
10255  }
10256  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10257  {
10258  if(NewRemainder[1] != 'R')
10259  {
10260  TimetableMessage(GiveMessages,
10261  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10262  OneEntry + "'");
10263  Utilities->CallLogPop(787);
10264  return(false);
10265  }
10266  }
10267  if((Second == "Snt") || (Second == "Snt-sh"))
10268  // need full train information including non-default values for at least HeadCode, Description,
10269  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10270  {
10271  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10272  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10273  {
10274  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10275  OneEntry + "'");
10276  Utilities->CallLogPop(1783);
10277  return(false);
10278  }
10279  }
10280  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10281  // service continuation - need at least non-default value for HeadCode
10282  {
10283  if(HeadCode == "")
10284  {
10285  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10286  OneEntry + "'");
10287  Utilities->CallLogPop(788);
10288  return(false);
10289  }
10290  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10291  {
10292  TimetableMessage(GiveMessages,
10293  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10294  OneEntry + "'");
10295  Utilities->CallLogPop(843);
10296  return(false);
10297  }
10298  }
10299  }
10300  if(SequenceType == Finish)
10301  {
10302  FinishFlag = true;
10303  // marker for only permitted additional entry being a repeat, only needed if the
10304  // finish entry is not the last entry
10305  }
10306  if(FinalCall)
10307  {
10308  // interpret & add to ActionVector
10309  TDateTime TempTime;
10310  TActionVectorEntry ActionVectorEntry;
10311  ActionVectorEntry.FormatType = FormatType;
10312  ActionVectorEntry.LocationType = LocationType;
10313  ActionVectorEntry.SequenceType = SequenceType;
10314  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10315  ActionVectorEntry.Warning = Warning;
10316  if(FormatType == TimeLoc)
10317  {
10318  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10319  {
10320  ;
10321  } // these will all be true as final call
10322 
10323  ActionVectorEntry.LocationName = Second;
10324  }
10325  else if(FormatType == PassTime) // new
10326  {
10327  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10328  {
10329  ;
10330  }
10331  ActionVectorEntry.Command = Second;
10332  ActionVectorEntry.LocationName = Third;
10333  }
10334  else if(FormatType == TimeTimeLoc)
10335  {
10336  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10337  {
10338  ;
10339  }
10340  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10341  {
10342  ;
10343  }
10344  ActionVectorEntry.LocationName = Third;
10345  }
10346  else if(FormatType == TimeCmd)
10347  {
10348  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10349  {
10350  ;
10351  }
10352  ActionVectorEntry.Command = Second;
10353  }
10354  else if(FormatType == ExitRailway)
10355  {
10356  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10357  {
10358  ;
10359  }
10360  ActionVectorEntry.Command = Second;
10361  ActionVectorEntry.ExitList = ExitList;
10362  }
10363  else if(FormatType == StartNew)
10364  {
10365  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10366  {
10367  ;
10368  }
10369  ActionVectorEntry.Command = Second;
10370  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10371  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10372  if(Fourth == 'S')
10373  {
10374  ActionVectorEntry.SignallerControl = true;
10375  }
10376  }
10377  else if(FormatType == SNTShuttle)
10378  {
10379  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10380  {
10381  ;
10382  }
10383  ActionVectorEntry.Command = Second;
10384  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10385  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10386  ActionVectorEntry.OtherHeadCode = Fourth;
10387  }
10388  else if(FormatType == SNSShuttle)
10389  {
10390  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10391  {
10392  ;
10393  }
10394  ActionVectorEntry.Command = Second;
10395  ActionVectorEntry.OtherHeadCode = Third;
10396  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10397  }
10398  else if(FormatType == TimeCmdHeadCode)
10399  {
10400  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10401  {
10402  ;
10403  }
10404  ActionVectorEntry.Command = Second;
10405  ActionVectorEntry.OtherHeadCode = Third;
10406  }
10407  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10408  {
10409  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10410  {
10411  ;
10412  }
10413  ActionVectorEntry.Command = Second;
10414  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10415  }
10416  else if(FormatType == FSHNewService)
10417  {
10418  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10419  {
10420  ;
10421  }
10422  ActionVectorEntry.Command = Second;
10423  ActionVectorEntry.OtherHeadCode = Third;
10424  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10425  }
10426  else if(FormatType == FinRemHere)
10427  {
10428  ActionVectorEntry.Command = Second;
10429  }
10430  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10431  }
10432  }
10433  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10434  {
10435  OneEntry = NewRemainder;
10436  First = "";
10437  Second = "";
10438  Third = "";
10439  Fourth = "";
10440  RearStartOrRepeatMins = 0;
10441  FrontStartOrRepeatDigits = 0;
10442  NumberOfRepeats = 0;
10443  if((FinishFlag) && (OneEntry[1] != 'R'))
10444  // already had a finish entry
10445  {
10446  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10447  Utilities->CallLogPop(79);
10448  return(false);
10449  }
10450  if(OneEntry[1] != 'R') // must be finish
10451  {
10452  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10453  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10454  {
10455  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10456  Utilities->CallLogPop(757);
10457  return(false);
10458  }
10459  if(SequenceType != Finish)
10460  {
10461  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10462  Utilities->CallLogPop(785);
10463  return(false);
10464  }
10465  if(FinalCall)
10466  {
10467  // interpret & add to ActionVector
10468  TDateTime TempTime;
10469  TActionVectorEntry ActionVectorEntry;
10470  ActionVectorEntry.FormatType = FormatType;
10471  ActionVectorEntry.LocationType = LocationType;
10472  ActionVectorEntry.SequenceType = SequenceType;
10473  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10474  ActionVectorEntry.Warning = Warning;
10475  if(FormatType == TimeCmd)
10476  {
10477  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10478  {
10479  ;
10480  }
10481  ActionVectorEntry.Command = Second;
10482  }
10483  else if(FormatType == TimeCmdHeadCode)
10484  {
10485  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10486  {
10487  ;
10488  }
10489  ActionVectorEntry.Command = Second;
10490  ActionVectorEntry.OtherHeadCode = Third;
10491  }
10492  else if(FormatType == FNSNonRepeatToShuttle)
10493  {
10494  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10495  {
10496  ;
10497  }
10498  ActionVectorEntry.Command = Second;
10499  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10500  }
10501  else if(FormatType == FSHNewService)
10502  {
10503  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10504  {
10505  ;
10506  }
10507  ActionVectorEntry.Command = Second;
10508  ActionVectorEntry.OtherHeadCode = Third;
10509  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10510  }
10511  else if(FormatType == ExitRailway)
10512  {
10513  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10514  {
10515  ;
10516  }
10517  ActionVectorEntry.Command = Second;
10518  ActionVectorEntry.ExitList = ExitList;
10519  }
10520  else if(FormatType == FinRemHere)
10521  {
10522  ActionVectorEntry.Command = Second;
10523  }
10524  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10525  }
10526  }
10527  else // repeat
10528  {
10529  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10530  {
10531  Utilities->CallLogPop(786);
10532  // error messages given in SplitRepeat
10533  return(false);
10534  }
10535  if(FinalCall)
10536  {
10537  TActionVectorEntry ActionVectorEntry;
10538  ActionVectorEntry.FormatType = Repeat;
10539  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10540  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10541  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10542  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10543  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10544  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10545  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10546  }
10547  }
10548  }
10549  }
10550  if(FinalCall)
10551  {
10552  TrainDataVector.push_back(TempTrainDataEntry);
10553  }
10554  }
10555  Utilities->CallLogPop(80);
10556  return(true);
10557 }
10558 
10559 // ---------------------------------------------------------------------------
10560 
10561 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10562 {
10563  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10564  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10565  {
10566  Utilities->CallLogPop(1890);
10567  return(false);
10568  }
10569  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10570  {
10571  Utilities->CallLogPop(1891);
10572  return(false);
10573  }
10574  Utilities->CallLogPop(1892);
10575  return(true);
10576 }
10577 
10578 // ---------------------------------------------------------------------------
10579 
10580 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10581 // 1st 5 chars must be HH:MM, anything else will be ignored
10582 {
10583  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10584  if(TimeStr.Length() < 5)
10585  {
10586  Utilities->CallLogPop(926);
10587  return(false);
10588  }
10589  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10590  {
10591  Utilities->CallLogPop(927);
10592  return(false);
10593  }
10594  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10595  {
10596  Utilities->CallLogPop(928);
10597  return(false);
10598  }
10599  if(TimeStr[3] != ':')
10600  {
10601  Utilities->CallLogPop(929);
10602  return(false);
10603  }
10604  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10605  {
10606  Utilities->CallLogPop(930);
10607  return(false);
10608  }
10609  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10610  {
10611  Utilities->CallLogPop(931);
10612  return(false);
10613  }
10614  while(TimeStr.Length() > 5)
10615  {
10616  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10617  }
10618  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10619  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10620 
10621  if((WholeHours + FracHour) >= 95.98334)
10622  {
10623  Utilities->CallLogPop(1817);
10624  return(false); // > 95h 59m
10625  }
10626  Time = TDateTime((WholeHours + FracHour) / 24);
10627  Utilities->CallLogPop(932);
10628  return(true);
10629 }
10630 
10631 // ---------------------------------------------------------------------------
10632 
10633 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10634  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10635  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
10636 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10637  Return false for failure.
10638  See description above under ProcessOneTimetableLinefor details of train action entries
10639  NB all types set except LocationType for Sns as may be located or not
10640 */{
10641  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10642  Warning = false;
10643  TDateTime TempTime;
10644 
10645  if(OneEntry.Length() > 0)
10646  {
10647  if(OneEntry[1] == 'W') // warning
10648  {
10649  Warning = true;
10650  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10651  // strip it off
10652  }
10653  }
10654  if(OneEntry == "Frh")
10655  {
10656  FormatType = FinRemHere;
10657  SequenceType = Finish;
10658  LocationType = AtLocation;
10659  ShuttleLinkType = NotAShuttleLink;
10660  Second = "Frh";
10661  Utilities->CallLogPop(1016);
10662  return(true);
10663  }
10664  if(OneEntry.Length() < 7)
10665  {
10666  Utilities->CallLogPop(907);
10667  return(false); // 'HH:MM;' + at least a one-letter location name
10668  }
10669  int Pos = OneEntry.Pos(';'); // first segment delimiter
10670 
10671  if(Pos != 6)
10672  {
10673  Utilities->CallLogPop(908);
10674  return(false);
10675  // no delimiter or delimiter not in position 6, has to be a time so fail
10676  }
10677  First = OneEntry.SubString(1, 5); // has to be a time
10678  if(!CheckTimeValidity(16, First, TempTime))
10679  {
10680  Utilities->CallLogPop(909);
10681  return(false);
10682  }
10683  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10684 
10685  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10686  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10687  {
10688  if(Remainder.Length() < 7)
10689  {
10690  Utilities->CallLogPop(910);
10691  return(false); // 'HH:MM;' + at least a one-letter location name
10692  }
10693  Pos = Remainder.Pos(';'); // second segment delimiter
10694  if(Pos == 0)
10695  {
10696  Utilities->CallLogPop(911);
10697  return(false);
10698  // no delimiter, has to be one between departure time & location
10699  }
10700  Second = Remainder.SubString(1, 5); // has to be a time
10701  if(!CheckTimeValidity(15, Second, TempTime))
10702  {
10703  Utilities->CallLogPop(912);
10704  return(false);
10705  }
10706  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10707  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10708  {
10709  Utilities->CallLogPop(913);
10710  return(false);
10711  }
10712  FormatType = TimeTimeLoc;
10713  SequenceType = Intermediate;
10714  LocationType = AtLocation;
10715  ShuttleLinkType = NotAShuttleLink;
10716  Utilities->CallLogPop(914);
10717  return(true);
10718  }
10719  Pos = Remainder.Pos(';'); // second segment delimiter
10720  if(Pos == 0) // no third segment so second must be a location, or cdt
10721  {
10722  Second = Remainder;
10723  if(Second == "cdt")
10724  {
10725  FormatType = TimeCmd;
10726  ShuttleLinkType = NotAShuttleLink;
10727  LocationType = AtLocation;
10728  SequenceType = Intermediate;
10729  Utilities->CallLogPop(915);
10730  return(true);
10731  }
10732  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10733  {
10734  Utilities->CallLogPop(916);
10735  return(false);
10736  }
10737  else
10738  {
10739  FormatType = TimeLoc;
10740  LocationType = AtLocation;
10741  SequenceType = Intermediate;
10742  ShuttleLinkType = NotAShuttleLink;
10743  Utilities->CallLogPop(917);
10744  return(true);
10745  }
10746  }
10747  // here if second segment is a command, with a third & maybe fourth segments as details
10748  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10749  {
10750  Utilities->CallLogPop(918);
10751  return(false);
10752  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10753  }
10754  Second = Remainder.SubString(1, Pos - 1); // command
10755 
10756  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10757  // details
10758  Pos = Remainder.Pos(';'); // third segment delimiter
10759  if(Pos == 0)
10760  {
10761  Third = Remainder;
10762  }
10763  else
10764  {
10765  Third = Remainder.SubString(1, Pos - 1);
10766  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10767  }
10768  if((Second == "Snt") || (Second == "Snt-sh"))
10769  // third has to be 2 element idents with a space between
10770  {
10771  int SpacePos = Third.Pos(' ');
10772  if(SpacePos == 0)
10773  {
10774  Utilities->CallLogPop(919);
10775  return(false); // no space
10776  }
10777  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
10778  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
10779  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
10780  if(CheckLocationsExistInRailway)
10781  {
10782  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
10783  {
10784  Utilities->CallLogPop(920);
10785  return(false);
10786  }
10787  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10788  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10789  }
10790  if(Second == "Snt")
10791  {
10792  FormatType = StartNew;
10793  SequenceType = Start;
10794  LocationType = NoLocation;
10795  // can't be set until know whether located or not - done in SecondPassActions
10796  ShuttleLinkType = NotAShuttleLink;
10797  }
10798  else // Snt-sh
10799  {
10800  FormatType = SNTShuttle;
10801  LocationType = AtLocation;
10802  SequenceType = Start;
10803  ShuttleLinkType = ShuttleLink;
10804  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10805  {
10806  Utilities->CallLogPop(1038);
10807  return(false);
10808  }
10809  }
10810  Utilities->CallLogPop(921);
10811  return(true);
10812  }
10813  if(Second == "Sns-sh") // third & fourth have to be headcodes
10814  {
10815  FormatType = SNSShuttle;
10816  LocationType = AtLocation;
10817  SequenceType = Start;
10818  ShuttleLinkType = ShuttleLink;
10819  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10820  {
10821  Utilities->CallLogPop(1039);
10822  return(false);
10823  }
10824  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10825  {
10826  Utilities->CallLogPop(1040);
10827  return(false);
10828  }
10829  Utilities->CallLogPop(1041);
10830  return(true);
10831  }
10832  if(Second == "F-nshs")
10833  {
10834  FormatType = FNSNonRepeatToShuttle;
10835  LocationType = AtLocation;
10836  SequenceType = Finish;
10837  ShuttleLinkType = ShuttleLink;
10838  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10839  {
10840  Utilities->CallLogPop(1047);
10841  return(false);
10842  }
10843  Utilities->CallLogPop(1048);
10844  return(true);
10845  }
10846  if(Second == "Sns-fsh")
10847  {
10848  FormatType = SNSNonRepeatFromShuttle;
10849  LocationType = AtLocation;
10850  SequenceType = Start;
10851  ShuttleLinkType = ShuttleLink;
10852  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10853  {
10854  Utilities->CallLogPop(1098);
10855  return(false);
10856  }
10857  Utilities->CallLogPop(1099);
10858  return(true);
10859  }
10860  if(Second == "Fns-sh") // third & fourth have to be headcodes
10861  {
10862  FormatType = FSHNewService;
10863  LocationType = AtLocation;
10864  SequenceType = Finish;
10865  ShuttleLinkType = ShuttleLink;
10866  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10867  {
10868  Utilities->CallLogPop(1050);
10869  return(false);
10870  }
10871  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10872  {
10873  Utilities->CallLogPop(1051);
10874  return(false);
10875  }
10876  Utilities->CallLogPop(1052);
10877  return(true);
10878  }
10879  // new segment for 'pas'
10880  if(Second == "pas") // third has to be a location
10881  {
10882  FormatType = PassTime;
10883  LocationType = EnRoute;
10884  SequenceType = Intermediate;
10885  ShuttleLinkType = NotAShuttleLink;
10886  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10887  {
10888  Utilities->CallLogPop(1515);
10889  return(false);
10890  }
10891  Utilities->CallLogPop(1516);
10892  return(true);
10893  }
10894  // new segment for revised 'Fer'
10895  if(Second == "Fer")
10896  // third has to be a set of IDs separated by spaces, and at least 1
10897  {
10898  FormatType = ExitRailway;
10899  LocationType = EnRoute;
10900  SequenceType = Finish;
10901  ShuttleLinkType = NotAShuttleLink;
10902  if(CheckLocationsExistInRailway)
10903  {
10904  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10905  {
10906  Utilities->CallLogPop(1519);
10907  return(false);
10908  }
10909  }
10910  Utilities->CallLogPop(1520);
10911  return(true);
10912  }
10913  // all remainder must be TimeCmdHeadCode types to be valid
10914  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10915  (Second != "Frh-sh"))
10916  {
10917  Utilities->CallLogPop(922);
10918  return(false); // all TimeCmdHeadCode types
10919  }
10920  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10921  {
10922  Utilities->CallLogPop(923);
10923  return(false);
10924  }
10925  FormatType = TimeCmdHeadCode;
10926  LocationType = AtLocation;
10927  if(Second == "Frh-sh")
10928  {
10929  ShuttleLinkType = ShuttleLink;
10930  }
10931  else
10932  {
10933  ShuttleLinkType = NotAShuttleLink;
10934  }
10935  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10936  {
10937  SequenceType = Finish;
10938  }
10939  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10940  {
10941  SequenceType = Intermediate;
10942  }
10943  if((Second == "Sfs") || (Second == "Sns"))
10944  {
10945  SequenceType = Start;
10946  }
10947  Utilities->CallLogPop(924);
10948  return(true);
10949 }
10950 
10951 // ---------------------------------------------------------------------------
10952 
10953 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10954 {
10955  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10956  // and contains no special characters
10957  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10958  if(LocStr == "")
10959  {
10960  Utilities->CallLogPop(1353);
10961  return(false); // has to have at least one character
10962  }
10963  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10964  {
10965  Utilities->CallLogPop(1354);
10966  return(false); // can't begin with a number
10967  }
10968  for(int x = 1; x < LocStr.Length() + 1; x++)
10969  {
10970  if(LocStr[x] < ' ')
10971  {
10972  Utilities->CallLogPop(1355);
10973  return(false); // contains a special character
10974  }
10975  if(LocStr[x] > 'z')
10976  {
10977  Utilities->CallLogPop(1356);
10978  return(false); // contains a character outside the standard ASCII set
10979  }
10980  }
10981  // check exists in railway location list if CheckLocationsExistInRailway is true
10982  if(CheckLocationsExistInRailway)
10983  {
10984  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10985  {
10986  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10987  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10988  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10989  "that includes a continuation will not be valid.");
10990  Utilities->CallLogPop(1357);
10991  return(false);
10992  }
10993  }
10994  Utilities->CallLogPop(1358);
10995  return(true);
10996 }
10997 
10998 // ---------------------------------------------------------------------------
10999 
11000 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11001 {
11002  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11003  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11004  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11005  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11006  HeadCode);
11007  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11008  {
11009  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11010  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
11011  Utilities->CallLogPop(1359);
11012  return(false);
11013  }
11014  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11015  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11016  {
11017  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11018  {
11019  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11020  Utilities->CallLogPop(1895);
11021  return(false);
11022  }
11023  }
11024  // secondly ensure the true Headcode only has letters or digits
11025  for(int x = 3; x >= 0; x--)
11026  {
11027  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11028  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11029  {
11030  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11031  Utilities->CallLogPop(1790);
11032  return(false);
11033  }
11034  }
11035  Utilities->CallLogPop(1364);
11036  return(true);
11037 }
11038 
11039 // ---------------------------------------------------------------------------
11040 
11041 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
11042 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11043 {
11044  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11045  ExitList.clear();
11046  AnsiString CurrentID = "";
11047 
11048  if(IDSet.Length() == 0)
11049  {
11050  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11051  Utilities->CallLogPop(1521);
11052  return(false);
11053  }
11054  for(int x = 1; x <= IDSet.Length(); x++)
11055  {
11056  char C = IDSet[x];
11057  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11058  {
11059  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11060  Utilities->CallLogPop(1522);
11061  return(false);
11062  }
11063  }
11064  int Pos = IDSet.Pos(' '); // look for the first space
11065 
11066  while(true)
11067  {
11068  if(Pos == 0)
11069  {
11070  CurrentID = IDSet;
11071  IDSet = "";
11072  }
11073  else
11074  {
11075  CurrentID = IDSet.SubString(1, Pos - 1);
11076  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11077  }
11078  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11079  if(VecPos == -1)
11080  {
11081  Utilities->CallLogPop(1523);
11082  return(false); // messages given in GetTrackVectorPositionFromString
11083  }
11084  else
11085  {
11086  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11087  {
11088  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11089  Utilities->CallLogPop(1524);
11090  return(false);
11091  }
11092  else
11093  {
11094  // first check for duplicates
11095  if(!ExitList.empty())
11096  {
11097  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11098  {
11099  if(*ELIT == VecPos)
11100  {
11101  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11102  Utilities->CallLogPop(1532);
11103  return(false);
11104  }
11105  }
11106  }
11107  // of OK add it to the list
11108  ExitList.push_back(VecPos);
11109  }
11110  }
11111  if(IDSet == "")
11112  {
11113  Utilities->CallLogPop(1525);
11114  return(true);
11115  }
11116  else
11117  {
11118  Pos = IDSet.Pos(' '); // look for the next space
11119  }
11120  } // while(true)
11121 }
11122 
11123 // ---------------------------------------------------------------------------
11124 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11125  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11126 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11127 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11128 // of each item
11129 {
11130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11131  int Pos = 0;
11132  AnsiString Remainder = "";
11133  int SemiColonCount = 0;
11134 
11135  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11136  {
11137  if(TrainInfoStr[x] == ';')
11138  {
11139  SemiColonCount++;
11140  }
11141  }
11142  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11143  {
11144  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11145  "'. Should be headcode + optional description for a continuing service;" +
11146  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11147  Utilities->CallLogPop(880);
11148  return(false);
11149  }
11150  if(SemiColonCount == 0)
11151  {
11152  HeadCode = TrainInfoStr;
11153  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11154  {
11155  Utilities->CallLogPop(881);
11156  return(false);
11157  }
11158  Utilities->CallLogPop(882);
11159  return(true);
11160  }
11161  if(SemiColonCount == 1) // headcode & description only
11162  {
11163  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11164  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11165  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11166  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11167  {
11168  Utilities->CallLogPop(883);
11169  return(false);
11170  }
11171  if(Description == "")
11172  {
11173  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11174  Utilities->CallLogPop(884);
11175  return(false);
11176  }
11177  if(Description.Length() > 60)
11178  {
11179  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11180  Utilities->CallLogPop(1157);
11181  return(false);
11182  }
11183  for(int x = 1; x < Description.Length() + 1; x++)
11184  {
11185  if((Description[x] < ' ') || (Description[x] > '~'))
11186  {
11187  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11188  Utilities->CallLogPop(885);
11189  return(false);
11190  }
11191  }
11192  Utilities->CallLogPop(886);
11193  return(true);
11194  }
11195  // if here must have 6 or 7 semicolons
11196  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11197  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11198  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11199  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11200  {
11201  Utilities->CallLogPop(887);
11202  return(false);
11203  }
11204  Pos = Remainder.Pos(';'); // 2nd delimiter
11205  Description = Remainder.SubString(1, Pos - 1);
11206  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11207  if(Description == "")
11208  {
11209  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11210  Utilities->CallLogPop(888);
11211  return(false);
11212  }
11213  if(Description.Length() > 60)
11214  {
11215  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11216  Utilities->CallLogPop(1158);
11217  return(false);
11218  }
11219  for(int x = 1; x < Description.Length() + 1; x++)
11220  {
11221  if((Description[x] < ' ') || (Description[x] > 126))
11222  {
11223  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11224  Utilities->CallLogPop(889);
11225  return(false);
11226  }
11227  }
11228  Pos = Remainder.Pos(';'); // 3rd delimiter
11229  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11230 
11231  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11232  if(StartSpeedStr == "")
11233  {
11234  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11235  Utilities->CallLogPop(890);
11236  return(false);
11237  }
11238  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11239  {
11240  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11241  {
11242  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11243  Utilities->CallLogPop(891);
11244  return(false);
11245  }
11246  }
11247  StartSpeed = StartSpeedStr.ToInt();
11248  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11249  {
11250  StartSpeed = TTrain::MaximumSpeedLimit;
11251  if(!SSHigh) // added at v2.4.0
11252  {
11253  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11254  SSHigh = true;
11255  }
11256  }
11257  Pos = Remainder.Pos(';'); // 4th delimiter
11258  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11259 
11260  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11261  if(MaxRunningSpeedStr == "")
11262  {
11263  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11264  Utilities->CallLogPop(892);
11265  return(false);
11266  }
11267  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11268  {
11269  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11270  {
11271  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11272  Utilities->CallLogPop(893);
11273  return(false);
11274  }
11275  }
11276  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11277  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11278  {
11279  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11280  if(!MRSHigh) // added at v2.4.0
11281  {
11282  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11283  MRSHigh = true;
11284  }
11285  }
11286  if(MaxRunningSpeed < 10)
11287  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11288  {
11289  MaxRunningSpeed = 10;
11290  if(!MRSLow) // added at v2.4.0
11291  {
11292  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11293  MRSLow = true;
11294  }
11295  }
11296  Pos = Remainder.Pos(';'); // 5th delimiter
11297  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11298 
11299  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11300  if(MassStr == "")
11301  {
11302  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11303  Utilities->CallLogPop(895);
11304  return(false);
11305  }
11306  for(int x = 1; x < MassStr.Length() + 1; x++)
11307  {
11308  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11309  {
11310  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11311  Utilities->CallLogPop(896);
11312  return(false);
11313  }
11314  }
11315  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11316  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11317  {
11318  Mass = TTrain::MaximumMassLimit;
11319  if(!MassHigh) // added at v2.4.0
11320  {
11321  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11322  MassHigh = true;
11323  }
11324  }
11325  if(Mass == 0)
11326  {
11327  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11328  Utilities->CallLogPop(897);
11329  return(false);
11330  }
11331  Pos = Remainder.Pos(';'); // 6th delimiter
11332  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11333 
11334  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11335  if(MaxBrakeForceStr == "")
11336  {
11337  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11338  Utilities->CallLogPop(898);
11339  return(false);
11340  }
11341  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11342  {
11343  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11344  {
11345  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11346  Utilities->CallLogPop(899);
11347  return(false);
11348  }
11349  }
11350  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11351 
11352  // convert to kg force
11353  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11354  {
11355  MaxBrakeForce = Mass;
11356  if(!BFHigh) // added at v2.4.0
11357  {
11358  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11359  BFHigh = true;
11360  }
11361  }
11362  if((MaxBrakeForce / Mass) < 0.01)
11363  {
11364  MaxBrakeForce = Mass * 0.01;
11365  if(!BFLow) // added at v2.4.0
11366  {
11367  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11368  BFLow = true;
11369  }
11370  }
11371  // convert to m/s/s
11372  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11373  // now may have just a power entry or power and signaller max. speed
11374  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11375 
11376  if(SemiColonCount == 6)
11377  {
11378  GrossPowerStr = Remainder;
11379  SignallerSpeedStr = "30"; // default value
11380  }
11381  else // must be 7
11382  {
11383  Pos = Remainder.Pos(';'); // 7th delimiter
11384  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11385  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11386  }
11387  // deal with GrossPower
11388  if(GrossPowerStr == "")
11389  {
11390  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11391  Utilities->CallLogPop(901);
11392  return(false);
11393  }
11394  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11395  {
11396  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11397  {
11398  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11399  Utilities->CallLogPop(902);
11400  return(false);
11401  }
11402  }
11403 
11404  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11405 
11406  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11407  {
11408  GrossPower = TTrain::MaximumPowerLimit;
11409  if(!PwrHigh)
11410  {
11411  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11412  PwrHigh = true;
11413  }
11414  }
11415  else if(GrossPower == 0) // changed at v2.4.0
11416  {
11417  GrossPower = 0.1;
11418  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11419  }
11420  else if((GrossPower > 0) && (GrossPower < 10000))
11421  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11422  {
11423  GrossPower = 10000;
11424  }
11425  PowerAtRail = GrossPower * 0.8;
11426  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11427 
11428  // deal with SignallerSpeed
11429  if(SignallerSpeedStr == "")
11430  {
11431  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11432  Utilities->CallLogPop(1771);
11433  return(false);
11434  }
11435  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11436  {
11437  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11438  {
11439  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11440  Utilities->CallLogPop(1769);
11441  return(false);
11442  }
11443  }
11444  SignallerSpeed = SignallerSpeedStr.ToInt();
11445  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11446  {
11447  SignallerSpeed = TTrain::MaximumSpeedLimit;
11448  if(!SigSHigh)
11449  {
11450  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11451  SigSHigh = true;
11452  }
11453  }
11454  if(SignallerSpeed < 10)
11455  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11456  {
11457  SignallerSpeed = 10;
11458  if(!SigSLow)
11459  {
11460  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11461  SigSLow = true;
11462  }
11463  // Utilities->CallLogPop(1770);
11464  // return false;
11465  }
11466  Utilities->CallLogPop(904);
11467  return(true);
11468 }
11469 
11470 // ---------------------------------------------------------------------------
11471 
11472 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11473  bool GiveMessages)
11474 {
11475  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11476  // function checks validity of each item and returns false for error
11477  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11478  if(OneEntry.Length() < 7)
11479  {
11480  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11481  Utilities->CallLogPop(865);
11482  return(false);
11483  }
11484  int SemiColonCount = 0;
11485 
11486  for(int x = 1; x < OneEntry.Length() + 1; x++)
11487  {
11488  if(OneEntry[x] == ';')
11489  {
11490  SemiColonCount++;
11491  }
11492  }
11493  if(SemiColonCount != 3)
11494  {
11495  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11496  Utilities->CallLogPop(866);
11497  return(false);
11498  }
11499  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11500  {
11501  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11502  Utilities->CallLogPop(867);
11503  return(false);
11504  }
11505  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11506  // strip off R;
11507 
11508  int Pos = 0;
11509 
11510  Pos = Remainder.Pos(';');
11511  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11512 
11513  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11514  if(MinutesStr == "")
11515  {
11516  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11517  Utilities->CallLogPop(868);
11518  return(false);
11519  }
11520  if(MinutesStr.Length() > 3)
11521  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11522  {
11523  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11524  Utilities->CallLogPop(2119);
11525  return(false);
11526  }
11527  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11528  {
11529  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11530  {
11531  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11532  Utilities->CallLogPop(869);
11533  return(false);
11534  }
11535  }
11536  RearStartOrRepeatMins = MinutesStr.ToInt();
11537  if(RearStartOrRepeatMins == 0)
11538  {
11539  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11540  Utilities->CallLogPop(870);
11541  return(false);
11542  }
11543  Pos = Remainder.Pos(';');
11544  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11545 
11546  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11547  if(DigitsStr == "")
11548  {
11549  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11550  Utilities->CallLogPop(871);
11551  return(false);
11552  }
11553  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11554  {
11555  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11556  {
11557  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11558  Utilities->CallLogPop(872);
11559  return(false);
11560  }
11561  }
11562  if(DigitsStr.Length() > 2)
11563  {
11564  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11565  Utilities->CallLogPop(873);
11566  return(false);
11567  }
11568  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11569 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11570  route rather than the service
11571  if(FrontStartOrRepeatDigits == 0)
11572  {
11573  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11574  Utilities->CallLogPop(874);
11575  return false;
11576  }
11577 */
11578  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11579  // new for v0.6b for unrestricted headcodes
11580  {
11581  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11582  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11583  Utilities->CallLogPop(1889);
11584  return(false);
11585  }
11586  AnsiString NumberStr = Remainder;
11587 
11588  if(NumberStr == "")
11589  {
11590  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11591  Utilities->CallLogPop(875);
11592  return(false);
11593  }
11594  if(NumberStr.Length() > 4)
11595  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11596  {
11597  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11598  Utilities->CallLogPop(2118);
11599  return(false);
11600  }
11601  for(int x = 1; x < NumberStr.Length() + 1; x++)
11602  {
11603  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11604  // catches negative numbers
11605  {
11606  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11607  Utilities->CallLogPop(876);
11608  return(false);
11609  }
11610  }
11611  NumberOfRepeats = NumberStr.ToInt();
11612  if(NumberOfRepeats == 0)
11613  {
11614  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11615  Utilities->CallLogPop(877);
11616  return(false);
11617  }
11618  Utilities->CallLogPop(878);
11619  return(true);
11620 }
11621 
11622 // ---------------------------------------------------------------------------
11623 
11624 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
11625 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11626  vector rather than the timetable
11627  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11628 
11629  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11630 
11631  For info:-
11632  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11633  {
11634  public:
11635  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
11637  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11638  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11639  int NumberOfRepeats; ///< the number of repeating services
11640  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11642  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11644  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11645  TTimetableFormatType FormatType; ///< defines the timetable action type
11646  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11647  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11648  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11649  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11651  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11653 
11654  // inline function
11655 
11657  TActionVectorEntry() {
11658  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11659  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11660  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11661  Warning = false; SignallerControl = false;
11662  }
11663  };
11664 
11665  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11666 
11667  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11668  {
11669  public:
11670  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11673  double MaxBrakeRate; ///< in metres/sec/sec
11674  double MaxRunningSpeed; ///< in km/h
11675  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11676  int Mass; ///< in kg
11677  int NumberOfTrains; ///< number of repeats + 1
11678  int SignallerSpeed; ///< in km/h for use when under signaller control
11679  int StartSpeed; ///< in km/h
11680  TActionVector ActionVector; ///< all the actions for the train
11681  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11682 
11683  //inline function
11684 
11686  TTrainDataEntry()
11687  {
11688  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11689  }
11690  };
11691 
11692  Allowable successors:-
11693  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11694  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11695  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11696  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11697  set location, else fails)
11698  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11699  set location, else fails)
11700  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11701  set location, else fails)
11702  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11703  set location, else fails)
11704  Fns -> R only
11705  F-nshs -> Nothing (no repeats permitted)
11706  Fjo -> R only
11707  Frh -> R only
11708  Fer -> R only
11709  Frh-sh -> R only
11710  Fns-sh -> R only
11711  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11712  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11713  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11714  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11715  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11716  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11717  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11718  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11719  Repeat -> Nothing
11720 
11721  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11722  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11723  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11724  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11725  Check all times increase or stay same through ActionVector
11726  Cycle through all entries in vector setting arr & dep times based on above list
11727  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11728  Check locations match the arr & dep TimeLoc entries
11729  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11730  Make above valid succession checks
11731  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11732  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11733  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11734  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11735  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11736  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11737  Set train info for Sfs & Sns entries
11738  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11739  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11740  element at each end, or length of 3 & 1 extra element at either end
11741  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11742  Check all Cmds have EventTime set & Arr & Dep times = -1
11743  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11744  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11745 
11746  Give messages in function if errors detected and clear the vector. Return false for failure.
11747 */
11748 
11749 /* Earlier checks:-
11750  Checks carried out with error messages in this function:-
11751  At least one comma in the line (it's based on a csv file);
11752  No entries following train information;
11753  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11754  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11755  First entry not a start entry;
11756  Train information incomplete before a start entry;
11757  Entry follows a finish entry but doesn't begin with 'R';
11758  SplitEntry returns false in a finish entry - message repeats the entry for information;
11759  Last action entry isn't a finish entry.
11760 
11761  Function returns false with no message if:-
11762  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11763  time is found at all then an error message is given in the calling function);
11764  SplitTrainInfo returns false (message given in called function);
11765  SplitRepeat returns false (message given in called function).
11766 */{
11767  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
11768  if(TrainDataVector.empty())
11769  {
11770  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
11771  TrainDataVector.clear();
11772  Utilities->CallLogPop(1832);
11773  return(false);
11774  }
11775 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
11776  1) must have at least one actionvector entry
11777  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
11778  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
11779  4) first entry must be a start;
11780  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
11781  5) a start must be the first entry;
11782  6) a repeat entry must be the last;
11783  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
11784  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11785  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11786 */
11787 
11788  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
11789  TwoLocationFlag = false; //added at v2.9.1
11790  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11791  {
11792  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11793  if(TrainDataVector.at(x).ActionVector.empty())
11794  {
11795  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11796  TrainDataVector.clear();
11797  Utilities->CallLogPop(1833);
11798  return(false);
11799  }
11800  }
11801  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11802  {
11803  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11804  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11805  if(!(AVEntry0.SignallerControl))
11806  {
11807  if(TrainDataVector.at(x).ActionVector.size() == 1)
11808  {
11809  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11810  TrainDataVector.clear();
11811  Utilities->CallLogPop(1822);
11812  return(false);
11813  }
11814  }
11815  }
11816  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11817  {
11818  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11819  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11820  if(AVEntry0.SignallerControl)
11821  {
11822  if(TrainDataVector.at(x).ActionVector.size() > 2)
11823  {
11824  SecondPassMessage(GiveMessages,
11825  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11826  TDEntry.HeadCode);
11827  TrainDataVector.clear();
11828  Utilities->CallLogPop(1837);
11829  return(false);
11830  }
11831  if(TrainDataVector.at(x).ActionVector.size() > 1)
11832  {
11833  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11834  if(AVEntry1.FormatType != Repeat)
11835  {
11836  SecondPassMessage(GiveMessages,
11837  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11838  TrainDataVector.clear();
11839  Utilities->CallLogPop(1838);
11840  return(false);
11841  }
11842  }
11843  }
11844  }
11845  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11846  {
11847  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11848  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11849  if(AVEntry0.SequenceType != Start)
11850  {
11851  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11852  TrainDataVector.clear();
11853  Utilities->CallLogPop(1824);
11854  return(false);
11855  }
11856  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11857  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11858  // and others for a located Snt, but those checks done later
11859  {
11860  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11861  // must be a second entry if first not signallercontrol
11862  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11863  {
11864  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11865  TDEntry.HeadCode);
11866  TrainDataVector.clear();
11867  Utilities->CallLogPop(2046);
11868  return(false);
11869  }
11870  }
11871  }
11872  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11873  {
11874  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11875  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11876  {
11877  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11878  if((AVEntry.SequenceType == Start) && (y != 0))
11879  {
11880  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11881  TrainDataVector.clear();
11882  Utilities->CallLogPop(1825);
11883  return(false);
11884  }
11885  }
11886  }
11887  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11888  {
11889  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11890  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11891  {
11892  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11893  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11894  {
11895  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11896  TrainDataVector.clear();
11897  Utilities->CallLogPop(1826);
11898  return(false);
11899  }
11900  }
11901  }
11902  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11903  {
11904  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11905  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11906  {
11907  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11908  if((y == 0) && AVEntry.SignallerControl)
11909  {
11910  break;
11911  }
11912  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11913  {
11914  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11915  {
11916  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11917  TrainDataVector.clear();
11918  Utilities->CallLogPop(1827);
11919  return(false);
11920  }
11921  if(AVEntry.FormatType == Repeat)
11922  {
11923  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11924  if(LastAVEntry.SequenceType != Finish)
11925  {
11926  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11927  TrainDataVector.clear();
11928  Utilities->CallLogPop(1828);
11929  return(false);
11930  }
11931  }
11932  }
11933  }
11934  }
11935  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11936  {
11937  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11938  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11939  {
11940  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11941  if(AVEntry.SequenceType == Finish)
11942  {
11943  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11944  {
11945  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11946  TrainDataVector.clear();
11947  Utilities->CallLogPop(1829);
11948  return(false);
11949  }
11950  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11951  {
11952  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11953  {
11954  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11955  TDEntry.HeadCode);
11956  TrainDataVector.clear();
11957  Utilities->CallLogPop(1830);
11958  return(false);
11959  }
11960  }
11961  }
11962  }
11963  }
11964 
11965  // end of new preliminary checks
11966 
11967  // check ActionVector present and check start event successor validity
11968  // For Snt & Snt-sh set location if stopped, don't set any times yet
11969  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11970  {
11971  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11972  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11973  // use reference so can change internals where necessary
11974  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11975  {
11976  AnsiString LocationName = "";
11977  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11978  // it is at a location
11979  {
11980  if(TDEntry.StartSpeed == 0) // stopped
11981  {
11982  AVEntry0.LocationName = LocationName;
11983  AVEntry0.LocationType = AtLocation;
11984  // check successor validity for located Snt that isn't a SignallerControl entry
11985  if(!AVEntry0.SignallerControl)
11986  {
11987  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11988  // at least 2 entries present checked in integrity check so (1) valid
11989  if(!AtLocSuccessor(AVEntry1))
11990  {
11991  // Frh following Snt-sh will return false in location check, so no need to check here
11992  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11993  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11994  TrainDataVector.clear();
11995  Utilities->CallLogPop(523);
11996  return(false);
11997  }
11998  }
11999  }
12000  else
12001  {
12002  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12003  TDEntry.HeadCode);
12004  TrainDataVector.clear();
12005  Utilities->CallLogPop(791);
12006  return(false);
12007  }
12008  }
12009  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12010  {
12011  if(AVEntry0.Command == "Snt-sh")
12012  {
12013  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12014  TrainDataVector.clear();
12015  Utilities->CallLogPop(1042);
12016  return(false);
12017  }
12018  AVEntry0.LocationType = EnRoute;
12019  if(!AVEntry0.SignallerControl)
12020  {
12021  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12022  // at least 2 entries checked in integrity check so (1) valid
12023  if(!MovingSuccessor(AVEntry1))
12024  {
12025  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12026  TDEntry.HeadCode);
12027  TrainDataVector.clear();
12028  Utilities->CallLogPop(790);
12029  return(false);
12030  }
12031  }
12032  }
12033  }
12034  // check other start successors
12035  else if(AVEntry0.SequenceType == Start)
12036  {
12037  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12038  // at least 2 entries present checked in integrity check so (1) valid
12039  if(!AtLocSuccessor(AVEntry1))
12040  {
12041  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12042  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12043  TrainDataVector.clear();
12044  Utilities->CallLogPop(793);
12045  return(false);
12046  }
12047  }
12048  }
12049 
12050  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12051  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12052  {
12053  bool FoundFlag = false;
12054  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12055  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12056  // use reference so can change internals
12057  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12058  {
12059  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12060  {
12061  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12062  if(AVEntry2.FormatType == TimeLoc)
12063  {
12064  FoundFlag = true;
12065  AVEntry.LocationName = AVEntry2.LocationName;
12066  break;
12067  }
12068  }
12069  if(!FoundFlag)
12070  {
12071  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12072  TDEntry.HeadCode);
12073  TrainDataVector.clear();
12074  Utilities->CallLogPop(851);
12075  return(false);
12076  }
12077  }
12078  }
12079 
12080  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12081  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12082  {
12083  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12084  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12085  {
12086  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12087  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12088  {
12089  if(AVEntry.LocationName == "")
12090  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12091  {
12092  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12093  ": an event should have had a location name associated with it but it could not be found");
12094  TrainDataVector.clear();
12095  Utilities->CallLogPop(1831);
12096  return(false);
12097  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12098  }
12099  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12100  {
12101  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12102  // use reference so can change internals where necessary
12103  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12104  {
12105  AVEntry2.LocationName = AVEntry.LocationName;
12106  }
12107  else
12108  {
12109  break;
12110  }
12111  }
12112  }
12113  }
12114  }
12115  // all location names now set
12116 
12117  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12118  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12119  {
12120  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12121  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12122  {
12123  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12124  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12125  {
12126  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12127  // i.e at least one more, must be a repeat
12128  {
12129  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12130  {
12131  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12132  TrainDataVector.clear();
12133  Utilities->CallLogPop(798);
12134  return(false);
12135  }
12136  }
12137  }
12138  if(AVEntry.Command == "F-nshs")
12139  {
12140  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12141  // i.e has to be the last
12142  {
12143  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12144  TrainDataVector.clear();
12145  Utilities->CallLogPop(1049);
12146  return(false);
12147  }
12148  }
12149  if(AVEntry.Command == "pas")
12150  {
12151  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12152  {
12153  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12154  TrainDataVector.clear();
12155  Utilities->CallLogPop(1518);
12156  return(false);
12157  }
12158  }
12159  if(AVEntry.Command == "jbo")
12160  {
12161  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12162  {
12163  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12164  TrainDataVector.clear();
12165  Utilities->CallLogPop(800);
12166  return(false);
12167  }
12168  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12169  if(!AtLocSuccessor(AVEntry2))
12170  {
12171  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12172  ". The event isn't valid for a stationary train.");
12173  TrainDataVector.clear();
12174  Utilities->CallLogPop(801);
12175  return(false);
12176  }
12177  }
12178  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12179  {
12180  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12181  {
12182  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12183  TrainDataVector.clear();
12184  Utilities->CallLogPop(802);
12185  return(false);
12186  }
12187  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12188  if(!AtLocSuccessor(AVEntry2))
12189  {
12190  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12191  ". The event isn't valid for a stationary train.");
12192  TrainDataVector.clear();
12193  Utilities->CallLogPop(803);
12194  return(false);
12195  }
12196  }
12197  if(AVEntry.Command == "cdt")
12198  {
12199  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12200  {
12201  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12202  TrainDataVector.clear();
12203  Utilities->CallLogPop(804);
12204  return(false);
12205  }
12206  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12207  if(!AtLocSuccessor(AVEntry2))
12208  {
12209  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12210  ". The event isn't valid for a stationary train.");
12211  TrainDataVector.clear();
12212  Utilities->CallLogPop(805);
12213  return(false);
12214  }
12215  }
12216  if(AVEntry.FormatType == TimeTimeLoc)
12217  {
12218  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12219  {
12220  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12221  TrainDataVector.clear();
12222  Utilities->CallLogPop(806);
12223  return(false);
12224  }
12225  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12226  if(!MovingSuccessor(AVEntry2))
12227  {
12228  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12229  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12230  TrainDataVector.clear();
12231  Utilities->CallLogPop(807);
12232  return(false);
12233  }
12234  }
12235  if(AVEntry.FormatType == PassTime)
12236  {
12237  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12238  {
12239  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12240  TrainDataVector.clear();
12241  Utilities->CallLogPop(1530);
12242  return(false);
12243  }
12244  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12245  if(!MovingSuccessor(AVEntry2))
12246  {
12247  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12248  ". The event isn't valid for a moving train.");
12249  TrainDataVector.clear();
12250  Utilities->CallLogPop(1531);
12251  return(false);
12252  }
12253  }
12254  if(AVEntry.FormatType == Repeat)
12255  {
12256  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12257  {
12258  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12259  TrainDataVector.clear();
12260  Utilities->CallLogPop(808);
12261  return(false);
12262  }
12263  }
12264  }
12265  }
12266 
12267  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12268  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12269  {
12270  bool LastEntryIsAnArrival = false;
12271  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12272  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12273  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12274  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12275  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12276  {
12277  LastEntryIsAnArrival = false;
12278  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12279  {
12280  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12281  if(AVEntry.FormatType == TimeLoc)
12282  {
12283  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12284  {
12285  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12286  }
12287  if(LastEntryIsAnArrival)
12288  {
12289  AVEntry.DepartureTime = AVEntry.EventTime;
12290  AVEntry.EventTime = TDateTime(-1);
12291  LastEntryIsAnArrival = false;
12292  }
12293  else // last entry a departure
12294  {
12295  AVEntry.ArrivalTime = AVEntry.EventTime;
12296  AVEntry.EventTime = TDateTime(-1);
12297  LastEntryIsAnArrival = true;
12298  }
12299  }
12300  }
12301  }
12302  else // all others stopped at beginning
12303  {
12304  LastEntryIsAnArrival = true;
12305  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12306  {
12307  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12308  if(AVEntry.FormatType == TimeLoc)
12309  {
12310  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12311  {
12312  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12313  }
12314  if(LastEntryIsAnArrival)
12315  {
12316  AVEntry.DepartureTime = AVEntry.EventTime;
12317  AVEntry.EventTime = TDateTime(-1);
12318  LastEntryIsAnArrival = false;
12319  }
12320  else // last entry a departure
12321  {
12322  AVEntry.ArrivalTime = AVEntry.EventTime;
12323  AVEntry.EventTime = TDateTime(-1);
12324  LastEntryIsAnArrival = true;
12325  }
12326  }
12327  }
12328  }
12329  }
12330  // perform remaining successor checks for TimeLocs
12331  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12332  {
12333  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12334  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12335  {
12336  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12337  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12338  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12339  {
12340  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12341  {
12342  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12343  TrainDataVector.clear();
12344  Utilities->CallLogPop(809);
12345  return(false);
12346  }
12347  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12348  if(!AtLocSuccessor(AVEntry2))
12349  {
12350  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12351  ". The event isn't valid for a stationary train.");
12352  TrainDataVector.clear();
12353  Utilities->CallLogPop(810);
12354  return(false);
12355  }
12356  }
12357  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12358  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12359  {
12360  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12361  {
12362  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12363  TrainDataVector.clear();
12364  Utilities->CallLogPop(811);
12365  return(false);
12366  }
12367  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12368  if(!MovingSuccessor(AVEntry2))
12369  {
12370  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12371  ". The event isn't valid for a moving train.");
12372  TrainDataVector.clear();
12373  Utilities->CallLogPop(812);
12374  return(false);
12375  }
12376  }
12377  }
12378  }
12379 
12380  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12381  // & repeats have no times set
12382  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12383  {
12384  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12385  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12386  {
12387  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12388  if(AVEntry.FormatType == TimeLoc)
12389  {
12390  if(AVEntry.EventTime != TDateTime(-1))
12391  {
12392  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12393  }
12394  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12395  {
12396  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12397  }
12398  }
12399  if(AVEntry.FormatType == TimeTimeLoc)
12400  {
12401  if(AVEntry.EventTime != TDateTime(-1))
12402  {
12403  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12404  }
12405  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12406  {
12407  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12408  }
12409  }
12410  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12411  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12412  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12413  {
12414  if(AVEntry.EventTime == TDateTime(-1))
12415  {
12416  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12417  }
12418  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12419  {
12420  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12421  }
12422  }
12423  if(AVEntry.FormatType == Repeat)
12424  {
12425  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12426  {
12427  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12428  }
12429  }
12430  }
12431  }
12432 
12433  // check times stay same or increase, note that can have time of 0 if include midnight
12434  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12435  {
12436  TDateTime CurrentTime = TTClockTime; // the timetable start time
12437  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12438  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12439  {
12440  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12441  if(AVEntry.FormatType == Repeat)
12442  {
12443  break;
12444  }
12445  if(AVEntry.FormatType == FinRemHere)
12446  {
12447  break;
12448  }
12449  if(AVEntry.FormatType == TimeTimeLoc)
12450  {
12451  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12452  {
12453  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12454  TDEntry.HeadCode);
12455  TrainDataVector.clear();
12456  Utilities->CallLogPop(813);
12457  return(false);
12458  }
12459  if(AVEntry.ArrivalTime < CurrentTime)
12460  {
12461  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12462  TDEntry.HeadCode);
12463  TrainDataVector.clear();
12464  Utilities->CallLogPop(814);
12465  return(false);
12466  }
12467  CurrentTime = AVEntry.DepartureTime;
12468  continue;
12469  }
12470  if(AVEntry.FormatType == TimeLoc)
12471  {
12472  if(AVEntry.ArrivalTime >= TDateTime(0))
12473  {
12474  if(AVEntry.ArrivalTime < CurrentTime)
12475  {
12476  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12477  TrainDataVector.clear();
12478  Utilities->CallLogPop(815);
12479  return(false);
12480  }
12481  CurrentTime = AVEntry.ArrivalTime;
12482  }
12483  else
12484  {
12485  if(AVEntry.DepartureTime < CurrentTime)
12486  // both may be 0 legitimately so must allow for this
12487  {
12488  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12489  TrainDataVector.clear();
12490  Utilities->CallLogPop(816);
12491  return(false);
12492  }
12493  CurrentTime = AVEntry.DepartureTime;
12494  }
12495  continue;
12496  }
12497  if(AVEntry.EventTime < CurrentTime)
12498  // all others have EventTime set
12499  {
12500  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12501  ", may be before timetable start time");
12502  TrainDataVector.clear();
12503  Utilities->CallLogPop(835);
12504  return(false);
12505  }
12506  CurrentTime = AVEntry.EventTime;
12507  continue;
12508  }
12509  }
12510 
12511  // check locations consistent
12512  AnsiString LastLocationName = "";
12513 
12514  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12515  {
12516  bool LastEntryIsAnArrival = false;
12517  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12518  // first deal with moving Snt entries (all else stopped)
12519  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12520  {
12521  LastEntryIsAnArrival = false;
12522  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12523  if(LastLocationName != "")
12524  {
12525  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12526  }
12527  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12528  y++) // note that immediate successor to a moving Snt can only be a Moving type
12529  {
12530  // if it's a SignallerControl entry then the condition isn't met
12531  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12532  if(AVEntry.FormatType == Repeat)
12533  {
12534  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12535  }
12536  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12537  {
12538  if(AVEntry.LocationName != LastLocationName)
12539  {
12540  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12541  AVEntry.Command);
12542  TrainDataVector.clear();
12543  Utilities->CallLogPop(823);
12544  return(false);
12545  }
12546  }
12547  else if(AVEntry.FormatType == TimeCmd)
12548  // cdt is the only TimeCmd
12549  {
12550  if(AVEntry.LocationName != LastLocationName)
12551  {
12552  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12553  AVEntry.Command);
12554  TrainDataVector.clear();
12555  Utilities->CallLogPop(824);
12556  return(false);
12557  }
12558  }
12559  else if(AVEntry.FormatType == TimeTimeLoc)
12560  {
12561  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12562  // last entry must be a departure or would have failed earlier
12563  {
12564  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12565  TwoLocationFlag = true;
12566 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12567 // TwoOrMoreLocationsWarningGiven = true;
12568 
12569 
12570  }
12571  LastLocationName = AVEntry.LocationName;
12572  LastEntryIsAnArrival = false;
12573  }
12574  else if(AVEntry.FormatType == TimeLoc)
12575  {
12576  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12577  {
12578  SecondPassMessage(GiveMessages,
12579  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12580  TrainDataVector.clear();
12581  Utilities->CallLogPop(826);
12582  return(false);
12583  }
12584  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12585  {
12586  SecondPassMessage(GiveMessages,
12587  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12588  TrainDataVector.clear();
12589  Utilities->CallLogPop(827);
12590  return(false);
12591  }
12592  LastLocationName = AVEntry.LocationName;
12593  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12594  }
12595  }
12596  }
12597  else // all stationary starting entries
12598  {
12599  LastEntryIsAnArrival = true;
12600  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12601  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12602  {
12603  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12604  if(AVEntry.FormatType == Repeat)
12605  {
12606  break;
12607  }
12608  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12609  // no need to add anything for shuttle starts since they are at loc (0) anyway
12610  {
12611  if(AVEntry.LocationName != LastLocationName)
12612  {
12613  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12614  AVEntry.Command);
12615  TrainDataVector.clear();
12616  Utilities->CallLogPop(828);
12617  return(false);
12618  }
12619  }
12620  else if(AVEntry.FormatType == TimeCmd)
12621  // cdt is the only TimeCmd
12622  {
12623  if(AVEntry.LocationName != LastLocationName)
12624  {
12625  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12626  AVEntry.Command);
12627  TrainDataVector.clear();
12628  Utilities->CallLogPop(829);
12629  return(false);
12630  }
12631  }
12632  else if(AVEntry.FormatType == TimeTimeLoc)
12633  {
12634  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12635  // last entry must be a departure or would have failed earlier
12636  {
12637  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12638  TwoLocationFlag = true;
12639 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12640 // TwoOrMoreLocationsWarningGiven = true;
12641  }
12642  LastLocationName = AVEntry.LocationName;
12643  LastEntryIsAnArrival = false;
12644  }
12645  else if(AVEntry.FormatType == TimeLoc)
12646  {
12647  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12648  {
12649  SecondPassMessage(GiveMessages,
12650  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12651  TrainDataVector.clear();
12652  Utilities->CallLogPop(831);
12653  return(false);
12654  }
12655  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12656  {
12657  SecondPassMessage(GiveMessages,
12658  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12660 // TrainDataVector.clear();
12661 // Utilities->CallLogPop(832);
12662 // return false;
12663  }
12664  LastLocationName = AVEntry.LocationName;
12665  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12666  }
12667  }
12668  }
12669  }
12670 
12671  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12672  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12673  AnsiString LocationNameToBeChecked = "";
12674 
12675  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12676  {
12677  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12678  unsigned int y = 0;
12679  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12680  // first discard unlocated Snt entries as they don't have location name set
12681  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12682  {
12683  y = 1;
12684  }
12685  while(y < TDEntry.ActionVector.size())
12686  // need to check each location name separately in turn, skipped for SignallerControl entries
12687  // if y == 1
12688  {
12689  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12690  {
12691  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12692  }
12693  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12694  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12695  {
12696  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12697  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12698  {
12699  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12700  }
12701  if(AVEntry.Command == "cdt")
12702  {
12703  break; // out of the 'z' loop since the check is only valid up to a change of direction
12704  }
12705  if(AVEntry.LocationName == LocationNameToBeChecked)
12706  {
12707  continue; // keep going while name same
12708  }
12709  if(AVEntry.LocationName != LocationNameToBeChecked)
12710  // if name different check forwards to see if repeats
12711  {
12712  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12713  {
12714  if(TDEntry.ActionVector.at(a).Command == "cdt")
12715  {
12716  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12717  }
12718  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12719  {
12720  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12721  TwoLocationFlag = true;
12722 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12723 // TwoOrMoreLocationsWarningGiven = true;
12724  }
12725  }
12726  break; // out of the 'z' loop since have checked 'a' as far as need to
12727  }
12728  }
12729  y++;
12730  }
12731  }
12732 
12733  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12734  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12735  {
12736  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12737  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12738  {
12739  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12740  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
12741  {
12742  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
12743  }
12744  AnsiString LocName = "";
12745  // dummy, only used so can call IsSNTEntryLocated
12746  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
12747  {
12748  if(AVEntry.LocationName == "")
12749  {
12750  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
12751  }
12752  }
12753  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
12754  {
12755  if(AVEntry.LocationName != "")
12756  {
12757  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
12758  }
12759  }
12760  }
12761  }
12762 
12763 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12764  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12765  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
12766 
12767  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
12768  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
12769 
12770  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
12771  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
12772  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
12773 */
12774  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
12775  {
12776  // non-shuttles & non-repeating links separately, but don't check that there isn't a
12777  // duplicate between a non-repeating shuttle and another - leave original tests in as
12778  // these also set the pointers
12779  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12780  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12781  {
12782  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12783  if(AVEntry.OtherHeadCode != "")
12784  {
12785  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
12786  {
12787  Utilities->CallLogPop(1584);
12788  return(false); // error message given in called function
12789  }
12790  }
12791  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12792  {
12793  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12794  {
12795  Utilities->CallLogPop(1585);
12796  return(false); // error message given in called function
12797  }
12798  }
12799  }
12800  }
12801 
12802  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12803  {
12804  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12805  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12806  {
12807  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12808  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12809  {
12810  if(AVEntry.OtherHeadCode != "")
12811  {
12812  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12813  // false = non-shuttle
12814  {
12815  Utilities->CallLogPop(864);
12816  return(false); // error message given in called function
12817  }
12818  }
12819  }
12820  }
12821  }
12822 
12823  // now repeat the check just for the shuttles
12824  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12825  {
12826  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12827  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12828  {
12829  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12830  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12831  {
12832  if(AVEntry.OtherHeadCode != "")
12833  {
12834  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12835  // true = shuttle
12836  {
12837  Utilities->CallLogPop(1100);
12838  return(false); // error message given in called function
12839  }
12840  }
12841  }
12842  }
12843  }
12844 
12845  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12846  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12847  {
12848  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12849  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12850  {
12851  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12852  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12853  {
12855  {
12856  Utilities->CallLogPop(1060);
12857  return(false); // error message given in called function
12858  }
12859  }
12860  }
12861  }
12862 
12863  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12864  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12865  // don't ever need to and as designed would skip repeats
12866  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12867  {
12868  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12869  {
12870  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12871  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12872  {
12873  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12874  {
12875  Utilities->CallLogPop(1090);
12876  return(false); // error message given in called function
12877  }
12878  }
12879  }
12880  }
12881 
12882  // check all entries have all types set to something
12883  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12884  {
12885  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12886  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12887  {
12888  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12889  if(AVEntry.FormatType == NoFormat)
12890  {
12891  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12892  }
12893  else if(AVEntry.SequenceType == NoSequence)
12894  {
12895  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12896  }
12897  else if(AVEntry.LocationType == NoLocation)
12898  {
12899  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12900  }
12901  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12902  {
12903  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12904  }
12905  }
12906  }
12907 
12908  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12909  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12910  {
12911  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12912  // non-const reference so can alter content
12913  TTrainOperatingData TData;
12914  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12915  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12916  {
12917 /*
12918  class TTrainOperatingData
12919  {
12920  public:
12921  int TrainID; - default, set at construction
12922  TActionEventType EventReported; used during operation
12923  TRunningEntry RunningEntry; - default, set at construction
12924  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12925  };
12926 */
12927  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12928  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12929  {
12930  TDEntry.TrainOperatingDataVector.push_back(TData);
12931  }
12932  }
12933  else
12934  {
12935  TDEntry.NumberOfTrains = 1;
12936  }
12937  }
12938 
12939  // check that don't include any Continuation names
12940  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12941  {
12942  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12943  {
12944  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12945  AnsiString HC = TrainDataVector.at(x).HeadCode;
12946  if(LocName != "")
12947  {
12948  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12949  {
12950  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12951  TrainDataVector.clear();
12952  Utilities->CallLogPop(1578);
12953  return(false);
12954  }
12955  }
12956  }
12957  }
12958 
12959  // check that all repeat times below 96h
12960  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12961  {
12962  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12963  int IncMinutes = 0;
12964  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12965  {
12966  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12967  }
12968  else
12969  {
12970  continue; // basic times already checked in CheckTimeValidity
12971  }
12972  AnsiString HC = TrainDataVector.at(x).HeadCode;
12973  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12974  {
12975  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12976  {
12977  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12978  {
12979  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12980  TrainDataVector.clear();
12981  Utilities->CallLogPop(1818);
12982  return(false);
12983  }
12984  }
12985  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12986  {
12987  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12988  // 3d 23h 59m = 3.9993055556
12989  {
12990  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12991  TrainDataVector.clear();
12992  Utilities->CallLogPop(1819);
12993  return(false);
12994  }
12995  }
12996  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12997  {
12998  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12999  // 3d 23h 59m = 3.9993055556
13000  {
13001  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13002  TrainDataVector.clear();
13003  Utilities->CallLogPop(1820);
13004  return(false);
13005  }
13006  }
13007  }
13008  }
13009 
13010  // Now that all set up change any extended headcodes back to ordinary headcodes
13011  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13012  {
13013  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13014  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13015  {
13016  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13017  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13018  }
13019  }
13020 
13021  // SaveTrainDataVectorToFile(0);//test
13023  Utilities->CallLogPop(782);
13024  return(true);
13025 }
13026 
13027 // ---------------------------------------------------------------------------
13028 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13030 {
13031  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13032 }
13033 
13034 // ---------------------------------------------------------------------------
13035 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13037 {
13038  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13039  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13040  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13041 }
13042 
13043 // ---------------------------------------------------------------------------
13044 
13045 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13046 {
13047  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13048  if(HeadCode.Length() > 4) // ignore otherwise
13049  {
13050  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13051  }
13052  Utilities->CallLogPop(1593);
13053 }
13054 
13055 // ---------------------------------------------------------------------------
13056 
13057 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13058 {
13059  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13060  SecondHeadCode);
13061  int ForwardCount = 0;
13062  int ReverseCount = 0;
13063 
13064  if(MainHeadCode == SecondHeadCode)
13065  {
13066  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13067  TrainDataVector.clear();
13068  Utilities->CallLogPop(1594);
13069  return(false);
13070  }
13071  // forward check
13072  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13073  {
13074  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13075  if(TDEntry.HeadCode == MainHeadCode)
13076  {
13077  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13078  {
13079  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13080  if(AVEntry.OtherHeadCode == SecondHeadCode)
13081  {
13082  ForwardCount++;
13083  }
13084  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13085  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13086  {
13087  ForwardCount++;
13088  }
13089  }
13090  }
13091  }
13092  if(ForwardCount == 0)
13093  // this is an exception because the headcodes are selected in the same order as the forward check
13094  {
13095  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13096  }
13097  if(ForwardCount > 2)
13098  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13099  {
13100  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13101  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13102  TrainDataVector.clear();
13103  Utilities->CallLogPop(1587);
13104  return(false);
13105  }
13106  // reverse check
13107  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13108  {
13109  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13110  if(TDEntry.HeadCode == SecondHeadCode)
13111  {
13112  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13113  {
13114  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13115  if(AVEntry.OtherHeadCode == MainHeadCode)
13116  {
13117  ReverseCount++;
13118  }
13119  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13120  {
13121  ReverseCount++;
13122  }
13123  }
13124  }
13125  }
13126 
13127  if(ReverseCount == 0)
13128  {
13129  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13130  TrainDataVector.clear();
13131  Utilities->CallLogPop(1588);
13132  return(false);
13133  }
13134  if(ReverseCount > 2)
13135  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13136  {
13137  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13138  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13139  TrainDataVector.clear();
13140  Utilities->CallLogPop(1589);
13141  return(false);
13142  }
13143  if(ForwardCount != ReverseCount)
13144  {
13145  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13146  " than the other way round");
13147  TrainDataVector.clear();
13148  Utilities->CallLogPop(1610);
13149  return(false);
13150  }
13151  Utilities->CallLogPop(1590);
13152  return(true);
13153 }
13154 
13155 // ---------------------------------------------------------------------------
13156 
13157 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13158 /* Return false for no find or more than one find, check correct types of link
13159  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13160  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13161  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13162  Then do the same in reverse.
13163  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13164  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13165  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13166  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13167  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13168 
13169  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13170  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13171 
13172  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13173 */
13174 
13175 {
13176  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13177  int ForwardCount = 0;
13178  int ReverseCount = 0;
13179  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13180  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13181  TTrainDataEntry *MainTrainDataPtr = 0;
13182  TTrainDataEntry *OtherTrainDataPtr = 0;
13183 
13184  // forward check
13185  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13186  {
13187  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13188  if(TDEntry.HeadCode == MainHeadCode)
13189  {
13190  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13191  {
13192  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13193  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13194  {
13195  if(AVEntry.OtherHeadCode == OtherHeadCode)
13196  {
13197  MainTrainDataPtr = &TrainDataVector.at(x);
13198  ForwardEntryPtr = &AVEntry;
13199  ForwardCount++;
13200  ForwardTDVectorNumber = x;
13201  }
13202  }
13203  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13204  (AVEntry.Command == "Frh-sh")))
13205  {
13206  if(AVEntry.OtherHeadCode == OtherHeadCode)
13207  {
13208  MainTrainDataPtr = &TrainDataVector.at(x);
13209  ForwardEntryPtr = &AVEntry;
13210  ForwardCount++;
13211  ForwardTDVectorNumber = x;
13212  }
13213  }
13214  }
13215  }
13216  }
13217  if(ForwardCount == 0)
13218  // this is an exception because the headcodes are selected in the same order as the forward check
13219  {
13220  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13221  }
13222  if(ForwardCount > 1)
13223  {
13224  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13225  MainHeadCode);
13226  TrainDataVector.clear();
13227  Utilities->CallLogPop(836);
13228  return(false);
13229  }
13230  // reverse check
13231  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13232  {
13233  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13234  if(TDEntry.HeadCode == OtherHeadCode)
13235  {
13236  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13237  {
13238  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13239  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13240  {
13241  if(AVEntry.OtherHeadCode == MainHeadCode)
13242  {
13243  OtherTrainDataPtr = &TrainDataVector.at(x);
13244  ReverseCount++;
13245  ReverseEntryPtr = &AVEntry;
13246  ReverseTDVectorNumber = x;
13247  }
13248  }
13249  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13250  {
13251  if(AVEntry.OtherHeadCode == MainHeadCode)
13252  {
13253  OtherTrainDataPtr = &TrainDataVector.at(x);
13254  ReverseCount++;
13255  ReverseEntryPtr = &AVEntry;
13256  ReverseTDVectorNumber = x;
13257  }
13258  }
13259  }
13260  }
13261  }
13262 
13263  if(ReverseCount == 0)
13264  {
13265  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13266  TrainDataVector.clear();
13267  Utilities->CallLogPop(837);
13268  return(false);
13269  }
13270  if(ReverseCount > 1)
13271  {
13272  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13273  OtherHeadCode);
13274  TrainDataVector.clear();
13275  Utilities->CallLogPop(838);
13276  return(false);
13277  }
13278  // these will all be false for !Shuttle
13279  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13280  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13281  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13282  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13283 
13284  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13285  {
13286  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13287  TrainDataVector.clear();
13288  Utilities->CallLogPop(1058);
13289  return(false);
13290  }
13291  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13292  {
13293  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13294  TrainDataVector.clear();
13295  Utilities->CallLogPop(1059);
13296  return(false);
13297  }
13298  if(ForwardEntryPtr->LocationName == "")
13299  {
13300  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13301  ". One or other service does not have a location set");
13302  TrainDataVector.clear();
13303  Utilities->CallLogPop(526);
13304  return(false);
13305  }
13306  if(ReverseEntryPtr->LocationName == "")
13307  {
13308  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13309  ". One or other service does not have a location set");
13310  TrainDataVector.clear();
13311  Utilities->CallLogPop(527);
13312  return(false);
13313  }
13314  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13315  {
13316  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13317  " is at a different location to the referencing train " + MainHeadCode);
13318  TrainDataVector.clear();
13319  Utilities->CallLogPop(842);
13320  return(false);
13321  }
13322  // ignore shuttle repeat links for first time check
13323  if(!Shuttle)
13324  {
13325  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13326  {
13327  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13328  " has a different event time to the referencing train " + MainHeadCode);
13329  TrainDataVector.clear();
13330  Utilities->CallLogPop(525);
13331  return(false);
13332  }
13333  }
13334  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13335  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13336  if(ForwardShuttleStart && ReverseShuttleFinish)
13337  // Shuttle must be true if these are true
13338  {
13339  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13340  {
13341  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13342  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13343  TrainDataVector.clear();
13344  Utilities->CallLogPop(1055);
13345  return(false);
13346  }
13347  }
13348  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13349  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13350  {
13351  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13352  {
13353  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13354  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13355  TrainDataVector.clear();
13356  Utilities->CallLogPop(528);
13357  return(false);
13358  }
13359  }
13360  if(ReverseEntryPtr->Command == "Fjo")
13361  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13362  {
13363  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13364  {
13365  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13366  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13367  TrainDataVector.clear();
13368  Utilities->CallLogPop(862);
13369  return(false);
13370  }
13371  }
13372  if(ReverseEntryPtr->Command == "Fns")
13373  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13374  {
13375  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13376  {
13377  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13378  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13379  TrainDataVector.clear();
13380  Utilities->CallLogPop(529);
13381  return(false);
13382  }
13383  }
13384  if(ForwardEntryPtr->Command == "Sfs")
13385  {
13386  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13387  {
13388  SecondPassMessage(GiveMessages,
13389  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13390  MainHeadCode);
13391  TrainDataVector.clear();
13392  Utilities->CallLogPop(530);
13393  return(false);
13394  }
13395  }
13396  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13397  {
13398  if(ReverseEntryPtr->Command != "Sfs")
13399  {
13400  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13401  MainHeadCode);
13402  TrainDataVector.clear();
13403  Utilities->CallLogPop(839);
13404  return(false);
13405  }
13406  else
13407  {
13408  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13409  {
13410  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13411  TrainDataVector.clear();
13412  Utilities->CallLogPop(849);
13413  return(false);
13414  }
13415  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13416  {
13417  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13418  TrainDataVector.clear();
13419  Utilities->CallLogPop(850);
13420  return(false);
13421  }
13422  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13423  {
13424  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13425  TrainDataVector.clear();
13426  Utilities->CallLogPop(846);
13427  return(false);
13428  }
13429  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13430  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13431  if(OtherTrainDataPtr->Description == "")
13432  {
13433  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13434  }
13435  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13436  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13437  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13438  }
13439  }
13440  if(ForwardEntryPtr->Command == "Sns")
13441  {
13442  if(ReverseEntryPtr->Command != "Fns")
13443  {
13444  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13445  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13446  TrainDataVector.clear();
13447  Utilities->CallLogPop(531);
13448  return(false);
13449  }
13450  }
13451  if(ForwardEntryPtr->Command == "Fns")
13452  {
13453  if(ReverseEntryPtr->Command != "Sns")
13454  {
13455  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13456  " and forms a new service with headcode " + OtherHeadCode);
13457  TrainDataVector.clear();
13458  Utilities->CallLogPop(840);
13459  return(false);
13460  }
13461  else
13462  {
13463  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13464  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13465  if(OtherTrainDataPtr->Description == "")
13466  {
13467  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13468  }
13469  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13470  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13471  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13472  }
13473  }
13474  if(ForwardEntryPtr->Command == "jbo")
13475  {
13476  if(ReverseEntryPtr->Command != "Fjo")
13477  {
13478  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13479  " and is joined by a train with headcode " + OtherHeadCode);
13480  TrainDataVector.clear();
13481  Utilities->CallLogPop(841);
13482  return(false);
13483  }
13484  }
13485  if(ForwardEntryPtr->Command == "Fjo")
13486  {
13487  if(ReverseEntryPtr->Command != "jbo")
13488  {
13489  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13490  " and joins a train with headcode " + OtherHeadCode);
13491  TrainDataVector.clear();
13492  Utilities->CallLogPop(532);
13493  return(false);
13494  }
13495  else
13496  {
13497  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13498  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13499  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13500  {
13501  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13502  }
13503  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13504  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13505  }
13506  }
13507  if(ForwardShuttleStart)
13508  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13509  {
13510  if(!ReverseShuttleFinish)
13511  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13512  {
13513  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13514  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13515  TrainDataVector.clear();
13516  Utilities->CallLogPop(1056);
13517  return(false);
13518  }
13519  }
13520  if(ReverseShuttleStart)
13521  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13522  {
13523  if(!ForwardShuttleFinish)
13524  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13525  {
13526  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13527  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13528  TrainDataVector.clear();
13529  Utilities->CallLogPop(1057);
13530  return(false);
13531  }
13532  else
13533  {
13534  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13535  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13536 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13537  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13538 */
13539  }
13540  }
13541  // check repeat information consistent if present
13542  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13543  // and those not accessed here
13544 
13545  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13546  bool MainRepeat = false, OtherRepeat = false;
13547  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13548 
13549  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13550  {
13551  MainRepeat = true;
13552  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13553  }
13554  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13555  {
13556  OtherRepeat = true;
13557  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13558  }
13559  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13560  {
13561  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13562  " and the associated train with headcode " + OtherHeadCode);
13563  TrainDataVector.clear();
13564  Utilities->CallLogPop(844);
13565  return(false);
13566  }
13567  if(MainRepeat && OtherRepeat)
13568  {
13569  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13570  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13571  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13572  {
13573  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13574  " and the associated train with headcode " + OtherHeadCode);
13575  TrainDataVector.clear();
13576  Utilities->CallLogPop(845);
13577  return(false);
13578  }
13579  }
13580  Utilities->CallLogPop(863);
13581  return(true);
13582 }
13583 
13584 // ---------------------------------------------------------------------------
13585 
13586 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13587 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13588 {
13589  // strip spaces from extreme ends of input
13590  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13591  if(Input == "")
13592  {
13593  Utilities->CallLogPop(856);
13594  return;
13595  }
13596  while(Input[1] == ' ')
13597  {
13598  if(Input.Length() > 1)
13599  {
13600  Input = Input.SubString(2, Input.Length() - 1);
13601  }
13602  else
13603  {
13604  Input = "";
13605  Utilities->CallLogPop(857);
13606  return;
13607  }
13608  }
13609  if(Input == "")
13610  {
13611  Utilities->CallLogPop(858);
13612  return;
13613  }
13614  while(Input[Input.Length()] == ' ')
13615  {
13616  if(Input.Length() > 1)
13617  {
13618  Input = Input.SubString(1, Input.Length() - 1);
13619  }
13620  else
13621  {
13622  Input = "";
13623  Utilities->CallLogPop(859);
13624  return;
13625  }
13626  }
13627  // now strip spaces immediately after all commas and semicolons within the text
13628  AnsiString Output = "";
13629  bool DelimiterFound = false;
13630 
13631  for(int x = 1; x < Input.Length() + 1; x++)
13632  {
13633  if(DelimiterFound)
13634  {
13635  if(Input[x] == ' ')
13636  {
13637  continue;
13638  }
13639  }
13640  if((Input[x] != ',') && (Input[x] != ';'))
13641  {
13642  DelimiterFound = false;
13643  Output = Output + Input[x];
13644  }
13645  else
13646  {
13647  DelimiterFound = true;
13648  Output = Output + Input[x];
13649  }
13650  }
13651  if(Output == "")
13652  {
13653  Input = "";
13654  Utilities->CallLogPop(860);
13655  return;
13656  }
13657  // now strip spaces immediately before all commas and semicolons within the text
13658  Input = Output;
13659  Output = "";
13660  DelimiterFound = false;
13661  for(int x = Input.Length(); x > 0; x--)
13662  {
13663  if(DelimiterFound)
13664  {
13665  if(Input[x] == ' ')
13666  {
13667  continue;
13668  }
13669  }
13670  if((Input[x] != ',') && (Input[x] != ';'))
13671  {
13672  DelimiterFound = false;
13673  Output = AnsiString(Input[x]) + Output;
13674  }
13675  else
13676  {
13677  DelimiterFound = true;
13678  Output = AnsiString(Input[x]) + Output;
13679  }
13680  }
13681  Input = Output;
13682  Utilities->CallLogPop(861);
13683 }
13684 
13685 // ---------------------------------------------------------------------------
13686 
13687 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13688 // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
13689 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13690 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13691 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13692 // are done in this function, they must be done elsewhere.
13693 {
13694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13695  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13696 
13697  LocationName = "";
13698  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13699  {
13700  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13701  }
13703  {
13704  Utilities->CallLogPop(852);
13705  return(false);
13706  }
13707  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13708  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13709 
13710  if(LocRear != "")
13711  {
13712  LocationName = LocRear;
13713  }
13714  else
13715  {
13716  LocationName = LocFront;
13717  }
13718  if(LocationName == "")
13719  {
13720  Utilities->CallLogPop(1036);
13721  return(false);
13722  }
13723  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
13724  {
13725  Utilities->CallLogPop(1773);
13726  return(true);
13727  }
13728  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
13729  {
13730  LocationName = "";
13731  Utilities->CallLogPop(1784);
13732  return(false);
13733  }
13734  // here if not a signaller start entry so must be at least one more entry
13735  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
13736 
13737  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
13738  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
13739  {
13740  //added F-nshs at v2.5.1 so can stay at a
13741  Utilities->CallLogPop(1037); //location until become a new shuttle service
13742  return(true); //added Fns at same time as saw no reason to exclude
13743  }
13744  AnsiString TimeLocLocationName;
13745  bool FoundFlag = false;
13746 
13747  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
13748  {
13749  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
13750  if(AVEntry.FormatType == TimeLoc)
13751  {
13752  FoundFlag = true;
13753  TimeLocLocationName = AVEntry.LocationName;
13754  break;
13755  }
13756  }
13757  if(!FoundFlag)
13758  {
13759  Utilities->CallLogPop(853);
13760  return(false);
13761  }
13762  if(TimeLocLocationName == LocationName)
13763  {
13764  Utilities->CallLogPop(854);
13765  return(true);
13766  }
13767  Utilities->CallLogPop(855);
13768  return(false);
13769 }
13770 
13771 // ---------------------------------------------------------------------------
13772 
13773 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
13774 {
13775  // checks that the new train start elements are valid - both exist & are connected, and that not
13776  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
13777  // & not starting with front on a continuation
13778  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
13779  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
13780 
13781  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
13782  if(RearPosition < 0)
13783  // error message given in GetTrackVectorPositionFromString
13784  {
13785  Utilities->CallLogPop(759);
13786  return(false);
13787  }
13788  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13789  if(FrontPosition < 0)
13790  // error message given in GetTrackVectorPositionFromString
13791  {
13792  Utilities->CallLogPop(760);
13793  return(false);
13794  }
13795  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13796  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13797  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13798 
13799  // check front & rear connected
13800  for(int x = 0; x < 4; x++)
13801  {
13802  if(RearTrackElement.Conn[x] == FrontPosition)
13803  {
13804  RearExitPos = x;
13805  break;
13806  }
13807  if(x == 3)
13808  {
13809  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13810  Utilities->CallLogPop(762);
13811  return(false);
13812  }
13813  }
13814  // check not starting with front on a continuation
13815  if(FrontType == Continuation)
13816  {
13817  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13818  Utilities->CallLogPop(937);
13819  return(false);
13820  }
13821  // check not starting on a level crossing
13822  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13823  {
13824  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13825  Utilities->CallLogPop(1951);
13826  return(false);
13827  }
13828  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13829  {
13830  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13831  Utilities->CallLogPop(1952);
13832  return(false);
13833  }
13834  // check if trying to start on diverging leg of points
13835  if((RearType == Points) && (RearExitPos == 3))
13836  {
13837  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13838  Utilities->CallLogPop(936);
13839  return(false);
13840  }
13841  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13842  {
13843  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13844  Utilities->CallLogPop(1808);
13845  return(false);
13846  }
13847  Utilities->CallLogPop(905);
13848  return(true);
13849 }
13850 
13851 // ---------------------------------------------------------------------------
13852 
13853 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13854 // Rear & front element validity already checked in CheckStartPositionValidity
13855 // This checks for points in correct orientation, no train at start position and not starting on a locked route
13856 {
13857  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13858  AnsiString(RearExitPos));
13859  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13860 
13861  if(RearTrackElement.TrackType == Continuation)
13862  {
13863  EventType = FailTrainEntry;
13864  }
13865  else
13866  {
13867  EventType = FailCreateTrain;
13868  }
13869  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13870  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13871  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13872  TTrackType RearType = RearTrackElement.TrackType;
13873  TTrackType FrontType = FrontTrackElement.TrackType;
13874  AnsiString RearName, FrontName;
13875 
13876  if(RearTrackElement.ActiveTrackElementName != "")
13877  {
13878  RearName = RearTrackElement.ActiveTrackElementName;
13879  }
13880  else
13881  {
13882  RearName = RearTrackElement.ElementID;
13883  }
13884  if(FrontTrackElement.ActiveTrackElementName != "")
13885  {
13886  FrontName = FrontTrackElement.ActiveTrackElementName;
13887  }
13888  else
13889  {
13890  FrontName = FrontTrackElement.ElementID;
13891  }
13892  TPrefDirElement PrefDirElement; // needed for next function but not used
13893  int LockedVectorNumber; // needed for next function but not used
13894 
13895  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13896  {
13897  if(ReportFlag)
13898  {
13899  if(EventType == FailCreateTrain)
13900  {
13901  EventType = FailCreateLockedRoute;
13902  }
13903  else
13904  {
13905  EventType = FailEnterLockedRoute;
13906  }
13907  LogActionError(47, HeadCode, "", EventType, FrontName);
13908  }
13909  Utilities->CallLogPop(940);
13910  return(false);
13911  }
13912  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13913  {
13914  if(ReportFlag)
13915  {
13916  if(EventType == FailCreateTrain)
13917  {
13918  EventType = FailCreateLockedRoute;
13919  }
13920  else
13921  {
13922  EventType = FailEnterLockedRoute;
13923  }
13924  LogActionError(48, HeadCode, "", EventType, RearName);
13925  }
13926  Utilities->CallLogPop(1809);
13927  return(false);
13928  }
13929  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13930  {
13931  if(ReportFlag)
13932  {
13933  LogActionError(27, HeadCode, "", EventType, RearName);
13934  }
13935  Utilities->CallLogPop(1810);
13936  return(false);
13937  }
13938  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13939  {
13940  if(ReportFlag)
13941  {
13942  if(EventType == FailCreateTrain)
13943  {
13944  LogActionError(28, HeadCode, "", EventType, FrontName);
13945  }
13946  else
13947  {
13948  LogActionError(43, HeadCode, "", EventType, RearName);
13949  }
13950  }
13951  Utilities->CallLogPop(941);
13952  return(false);
13953  }
13954  if(RearType == Bridge)
13955  {
13956  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13957  {
13958  if(ReportFlag)
13959  {
13960  LogActionError(29, HeadCode, "", EventType, RearName);
13961  }
13962  Utilities->CallLogPop(942);
13963  return(false);
13964  }
13965  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13966  {
13967  if(ReportFlag)
13968  {
13969  LogActionError(30, HeadCode, "", EventType, RearName);
13970  }
13971  Utilities->CallLogPop(943);
13972  return(false);
13973  }
13974  }
13975  if(FrontType == Bridge)
13976  {
13977  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13978  {
13979  if(ReportFlag)
13980  {
13981  if(EventType == FailCreateTrain)
13982  {
13983  LogActionError(31, HeadCode, "", EventType, FrontName);
13984  }
13985  else
13986  {
13987  LogActionError(44, HeadCode, "", EventType, RearName);
13988  }
13989  }
13990  Utilities->CallLogPop(944);
13991  return(false);
13992  }
13993  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13994  {
13995  if(ReportFlag)
13996  {
13997  if(EventType == FailCreateTrain)
13998  {
13999  LogActionError(45, HeadCode, "", EventType, FrontName);
14000  }
14001  else
14002  {
14003  LogActionError(46, HeadCode, "", EventType, RearName);
14004  }
14005  }
14006  Utilities->CallLogPop(945);
14007  return(false);
14008  }
14009  }
14010  EventType = FailCreatePoints;
14011  if(RearType == Points)
14012  {
14013  if(RearTrackElement.Attribute == 1)
14014  {
14015  if(ReportFlag)
14016  {
14017  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14018  }
14019  Utilities->CallLogPop(933);
14020  return(false);
14021  }
14022  }
14023  if(FrontType == Points)
14024  {
14025  if(FrontTrackElement.Attribute == 1)
14026  {
14027  if(ReportFlag)
14028  {
14029  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14030  }
14031  Utilities->CallLogPop(934);
14032  return(false);
14033  }
14034  }
14035 
14036  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14037  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14038  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14039  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14040  int RouteNumber; //not used
14041  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14042  {
14043  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14044  {
14045  EventType = FailEntryRouteSetAgainst;
14046  if(ReportFlag)
14047  {
14048  LogActionError(63, HeadCode, "", EventType, RearName);
14049  }
14050  Utilities->CallLogPop(2317);
14051  return(false);
14052  }
14053  }
14054  Utilities->CallLogPop(939);
14055  return(true);
14056 }
14057 
14058 // ---------------------------------------------------------------------------
14059 
14060 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14061 {
14062  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14063  "," + AnsiString(IncDigits));
14064  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14065  {
14066  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14067  }
14068  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14069  {
14070  Utilities->CallLogPop(1893);
14071  return(BaseHeadCode);
14072  }
14073  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14074  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14075 
14076  while(NextRepeatDigits >= 100)
14077  {
14078  NextRepeatDigits -= 100; // rolls over after 99
14079  }
14080  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14081 
14082  if(NextRepeatDigitsStr.Length() < 2)
14083  {
14084  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14085  }
14086  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14087 
14088  Utilities->CallLogPop(1365);
14089  return(NextRepeatHeadCode);
14090 }
14091 
14092 // ---------------------------------------------------------------------------
14093 
14094 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14095 {
14096  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14097  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14098  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14099 
14100  Utilities->CallLogPop(1366);
14101  return(NextRepeatTime);
14102 }
14103 
14104 // ---------------------------------------------------------------------------
14105 
14106 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14107 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14108 {
14109  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14110  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14111  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14112  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14113  int RepeatSecs = RepeatMinutes * 60;
14114 
14115  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14116  {
14117  Utilities->CallLogPop(1367);
14118  return(false);
14119  }
14120  else
14121  {
14122  Utilities->CallLogPop(1368);
14123  return(true);
14124  }
14125 }
14126 
14127 // ---------------------------------------------------------------------------
14128 
14129 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14130 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14131 {
14132  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14133  NonRepeatingHeadCode);
14134  int ForwardCount = 0;
14135  int ReverseCount = 0;
14136  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14137  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14138  // Forward corresponds to Main, Reverse to Other
14139  TTrainDataEntry *MainTrainDataPtr = 0;
14140  TTrainDataEntry *OtherTrainDataPtr = 0;
14141 
14142  // forward check
14143  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14144  {
14145  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14146  if(TDEntry.HeadCode == MainHeadCode)
14147  {
14148  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14149  {
14150  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14151  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14152  {
14153  MainTrainDataPtr = &TrainDataVector.at(x);
14154  ForwardEntryPtr = &AVEntry;
14155  ForwardCount++;
14156  ForwardTDVectorNumber = x;
14157  }
14158  }
14159  }
14160  }
14161  if(ForwardCount == 0)
14162  // this is an exception because the headcodes are selected in the same order as the forward check
14163  {
14164  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14165  }
14166  if(ForwardCount > 1)
14167  {
14168  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14169  MainHeadCode);
14170  TrainDataVector.clear();
14171  Utilities->CallLogPop(1061);
14172  return(false);
14173  }
14174  // reverse check
14175  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14176  {
14177  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14178  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14179  {
14180  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14181  {
14182  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14183  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14184  {
14185  OtherTrainDataPtr = &TrainDataVector.at(x);
14186  ReverseCount++;
14187  ReverseEntryPtr = &AVEntry;
14188  ReverseTDVectorNumber = x;
14189  }
14190  }
14191  }
14192  }
14193 
14194  if(ReverseCount == 0)
14195  {
14196  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14197  TrainDataVector.clear();
14198  Utilities->CallLogPop(1062);
14199  return(false);
14200  }
14201  if(ReverseCount > 1)
14202  {
14203  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14204  NonRepeatingHeadCode);
14205  TrainDataVector.clear();
14206  Utilities->CallLogPop(1063);
14207  return(false);
14208  }
14209  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14210  {
14211  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14212  TrainDataVector.clear();
14213  Utilities->CallLogPop(1064);
14214  return(false);
14215  }
14216  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14217  {
14218  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14219  TrainDataVector.clear();
14220  Utilities->CallLogPop(1065);
14221  return(false);
14222  }
14223  if(ForwardEntryPtr->LocationName == "")
14224  {
14225  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14226  ". One or other service does not have a location set");
14227  TrainDataVector.clear();
14228  Utilities->CallLogPop(1066);
14229  return(false);
14230  }
14231  if(ReverseEntryPtr->LocationName == "")
14232  {
14233  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14234  ". One or other service does not have a location set");
14235  TrainDataVector.clear();
14236  Utilities->CallLogPop(1067);
14237  return(false);
14238  }
14239  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14240  {
14241  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14242  " is at a different location to the referencing train " + MainHeadCode);
14243  TrainDataVector.clear();
14244  Utilities->CallLogPop(1068);
14245  return(false);
14246  }
14247  if(ForwardEntryPtr->Command == "F-nshs")
14248  // i.e. the non repeating link into the shuttle service
14249  {
14250  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14251  {
14252  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14253  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14254  TrainDataVector.clear();
14255  Utilities->CallLogPop(1069);
14256  return(false);
14257  }
14258  }
14259  if(ForwardEntryPtr->Command == "Fns-sh")
14260  // i.e. the non repeating link out from the shuttle service
14261  {
14262  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14263  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14264  {
14265  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14266  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14267  TrainDataVector.clear();
14268  Utilities->CallLogPop(1070);
14269  return(false);
14270  }
14271  }
14272  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14273  // i.e. a non repeating link to or from the shuttle service
14274  {
14275  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14276  {
14277  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14278  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14279  TrainDataVector.clear();
14280  Utilities->CallLogPop(1071);
14281  return(false);
14282  }
14283  }
14284 /* it's allowed to have a different description
14285  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14286  {
14287  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14288  {
14289  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14290  TrainDataVector.clear();
14291  Utilities->CallLogPop(1072);
14292  return false;
14293  }
14294  }
14295 */
14296  if(ForwardEntryPtr->Command == "Sns-sh")
14297  {
14298  if(ReverseEntryPtr->Command != "F-nshs")
14299  {
14300  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14301  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14302  TrainDataVector.clear();
14303  Utilities->CallLogPop(1073);
14304  return(false);
14305  }
14306  }
14307  if(ForwardEntryPtr->Command == "F-nshs")
14308  {
14309  if(ReverseEntryPtr->Command != "Sns-sh")
14310  {
14311  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14312  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14313  TrainDataVector.clear();
14314  Utilities->CallLogPop(1074);
14315  return(false);
14316  }
14317  else
14318  {
14319  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14320  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14321  if(OtherTrainDataPtr->Description == "")
14322  {
14323  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14324  }
14325  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14326  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14327  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14328  }
14329  }
14330  if(ForwardEntryPtr->Command == "Sns-fsh")
14331  {
14332  if(ReverseEntryPtr->Command != "Fns-sh")
14333  {
14334  SecondPassMessage(GiveMessages,
14335  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14336  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14337  TrainDataVector.clear();
14338  Utilities->CallLogPop(1075);
14339  return(false);
14340  }
14341  }
14342  if(ForwardEntryPtr->Command == "Fns-sh")
14343  {
14344  if(ReverseEntryPtr->Command != "Sns-fsh")
14345  {
14346  SecondPassMessage(GiveMessages,
14347  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14348  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14349  TrainDataVector.clear();
14350  Utilities->CallLogPop(1076);
14351  return(false);
14352  }
14353  else
14354  {
14355  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14356  // links to the non-repeating non-shuttle linked service
14357  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14358  // needed for creating formatted timetable
14359  if(OtherTrainDataPtr->Description == "")
14360  {
14361  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14362  }
14363  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14364  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14365  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14366  }
14367  }
14368  Utilities->CallLogPop(1077);
14369  return(true);
14370 }
14371 
14372 // ---------------------------------------------------------------------------
14373 
14374 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14375 // Forward train is the finish shuttle entry 'Fns-sh'.
14376 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14377 {
14378  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14379  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14380  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14381  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14382  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14383 
14384  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14385  {
14386  Utilities->CallLogPop(1369);
14387  return(false);
14388  }
14389  else
14390  {
14391  Utilities->CallLogPop(1370);
14392  return(true);
14393  }
14394 }
14395 
14396 // ---------------------------------------------------------------------------
14397 
14398 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14399 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14400 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14401 // don't ever need to and as designed would skip repeats.
14402 
14403 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14404 {
14405  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14406  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14407  {
14408  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14409  }
14410  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14411  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14412  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14413 
14414  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14415  {
14416  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14417  TrainDataVector.clear();
14418  Utilities->CallLogPop(1091);
14419  return(false);
14420  }
14421  while(LastActionCommand == "Fns")
14422  {
14423  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14424  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14425  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14426  {
14427  SecondPassMessage(GiveMessages,
14428  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14429  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14430  TrainDataVector.clear();
14431  Utilities->CallLogPop(1092);
14432  return(false);
14433  }
14434  }
14435  // exit the 'while' with LastActionCommand FSH-XX
14436  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14437  {
14438  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14439  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14440  ". The linking of two or more shuttles is not permitted.");
14441  TrainDataVector.clear();
14442  Utilities->CallLogPop(1093);
14443  return(false);
14444  }
14445  Utilities->CallLogPop(1094);
14446  return(true);
14447 }
14448 
14449 // ---------------------------------------------------------------------------
14450 
14451 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14452 {
14453  if(!GiveMessages)
14454  {
14455  return;
14456  }
14457  // if(ServiceReference == "") ShowMessage(Message);
14458  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14459  {
14460  ShowMessage(Message);
14461  }
14462  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14463  // false means don't give messages within the function
14464  else
14465  {
14466  ShowMessage("Service " + ServiceReference + ": " + Message);
14467  }
14468 }
14469 
14470 // ---------------------------------------------------------------------------
14471 
14472 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14473 {
14474  if(!GiveMessages)
14475  {
14476  return;
14477  }
14478  ShowMessage(Message);
14479 }
14480 
14481 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14482 // ---------------------------------------------------------------------------
14483 
14484 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14485 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14486 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14487 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14488 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14489 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14490 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14491 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14492 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14493 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14494 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14495 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14496 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14497 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14498 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14499 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14500 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14501 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14502 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14503 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14504 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14505 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14506 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14507 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14508 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14509 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14510 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14511 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14512 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14513 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14514 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5
14515 {
14516  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14517  AnsiString(ActionEventType) + "," + LocationID);
14518  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14519 
14520  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
14521  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
14522 
14523  Prefix = " ERROR: ";
14524  if(ActionEventType == FailTrainEntry)
14525  {
14526  Prefix = " HELD: ";
14527  ErrorLog = " can't enter railway, train obstructing entry position ";
14528  WarningStr = " can't enter railway, train obstructing entry position ";
14529  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
14530  }
14531  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
14532  {
14533  Prefix = " HELD: ";
14534  ErrorLog = " can't enter railway, route set against it at entry position ";
14535  WarningStr = " can't enter railway, route set against it at entry position ";
14536  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
14537  }
14538  else if(ActionEventType == FailCreateTrain)
14539  {
14540  Prefix = " HELD: ";
14541  ErrorLog = " can't be created, train obstructing ";
14542  WarningStr = " can't be created, train obstructing ";
14543  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
14544  }
14545  else if(ActionEventType == FailCreateLockedRoute)
14546  {
14547  Prefix = " HELD: ";
14548  ErrorLog = " can't be created on a locked route at ";
14549  WarningStr = " can't be created on a locked route at ";
14550  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
14551  }
14552  else if(ActionEventType == FailEnterLockedRoute)
14553  {
14554  Prefix = " HELD: ";
14555  ErrorLog = " can't enter on a locked route at ";
14556  WarningStr = " can't enter on a locked route at ";
14557  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
14558  }
14559  else if(ActionEventType == FailCreatePoints)
14560  {
14561  Prefix = " HELD: ";
14562  ErrorLog = " can't be created, diverging points at ";
14563  WarningStr = " can't be created, diverging points at ";
14564  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
14565  }
14566  else if(ActionEventType == FailUnexpectedExitRailway)
14567  {
14568  ErrorLog = " left railway unexpectedly at ";
14569  UnexpectedExits++;
14570  }
14571  else if(ActionEventType == FailIncorrectExit)
14572  {
14573  ErrorLog = " left railway at an incorrect exit at ";
14574  IncorrectExits++;
14575  }
14576  else if(ActionEventType == FailLocTooShort)
14577  {
14578  ErrorLog = " failed to split - location too short at ";
14579  WarningStr = " failed to split, location too short at ";
14580  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
14581  }
14582  else if(ActionEventType == FailSplitDueToOtherTrain)
14583  {
14584  Prefix = " HELD: ";
14585  ErrorLog = " unable to split - other train obstructing at ";
14586  WarningStr = " unable to split - other train obstructing at ";
14587  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
14588  }
14589  else if(ActionEventType == FailUnexpectedBuffers)
14590  {
14591  ErrorLog = " stopped at buffers unexpectedly at position ";
14592  }
14593  else if(ActionEventType == FailMissedArrival)
14594  {
14595  ErrorLog = " failed to stop at ";
14596  MissedStops++;
14597  }
14598  else if(ActionEventType == FailMissedSplit)
14599  {
14600  ErrorLog = " failed to split at ";
14602  }
14603  else if(ActionEventType == FailMissedJBO)
14604  {
14605  ErrorLog = " failed to be joined by other train at ";
14607  }
14608  else if(ActionEventType == FailMissedJoinOther)
14609  {
14610  ErrorLog = " failed to join other train at ";
14612  }
14613  else if(ActionEventType == FailMissedTerminate)
14614  {
14615  ErrorLog = " failed to terminate at ";
14617  }
14618  else if(ActionEventType == FailMissedNewService)
14619  {
14620  ErrorLog = " failed to form new service at ";
14622  }
14623  else if(ActionEventType == FailMissedExitRailway)
14624  {
14625  ErrorLog = " failed to exit railway ";
14627  }
14628  else if(ActionEventType == FailMissedChangeDirection)
14629  {
14630  ErrorLog = " failed to change direction at ";
14632  }
14633  else if(ActionEventType == FailMissedPass)
14634  {
14635  ErrorLog = " failed to pass ";
14637  }
14638  else if(ActionEventType == FailBuffersPreventingStart)
14639  {
14640  ErrorLog = " facing buffers and unable to start at ";
14641  }
14642  else if(ActionEventType == FailDerailed)
14643  {
14644  ErrorLog = " DERAILED at position ";
14645  Prefix = " DERAILMENT: ";
14646  Derailments++;
14647  }
14648  else if(ActionEventType == FailBufferCrash)
14649  {
14650  ErrorLog = " CRASHED INTO BUFFERS at ";
14651  Prefix = " CRASH: ";
14652  CrashedTrains++;
14653  }
14654  else if(ActionEventType == FailLevelCrossingCrash)
14655  {
14656  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14657  Prefix = " CRASH: ";
14658  CrashedTrains++;
14659  }
14660  else if(ActionEventType == FailCrashed)
14661  {
14662  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14663  Prefix = " CRASH: ";
14664  CrashedTrains++;
14665  CrashedTrains++;
14666  }
14667  else if(ActionEventType == FailSPAD)
14668  {
14669  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14670  Prefix = " SPAD: ";
14671  SPADEvents++;
14672  }
14673  else if(ActionEventType == FailLockedRoute)
14674  {
14675  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14676  Prefix = " SPAD RISK: ";
14677  SPADRisks++;
14678  }
14679  else if(ActionEventType == RouteForceCancelled)
14680  {
14681  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14682  }
14683  else if(ActionEventType == WaitingForJBO)
14684  {
14685  Prefix = " WARNING: ";
14686  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14687  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14688  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
14689  }
14690  else if(ActionEventType == WaitingForFJO)
14691  {
14692  Prefix = " WARNING: ";
14693  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14694  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14695  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
14696  }
14697 
14698  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14699  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14700  Utilities->CallLogPop(1371);
14701 }
14702 
14703 // ---------------------------------------------------------------------------
14704 
14706 {
14707 /*
14708  TrainDataEntry
14709  AnsiString HeadCode, Description;//null on creation
14710  int StartSpeed, MaxRunningSpeed;//both kph
14711  int RepeatNumber;
14712  TActionVector ActionVector;
14713  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14714  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14715 
14716  ActionVectorEntry
14717  TTimetableEntryType FormatType;
14718  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
14719  AnsiString LocationName, Command, OtherHeadCode;//null on creation
14720  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
14721  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14722  int RepeatNumber;
14723 
14724  TrainOperatingData
14725  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
14726  int TrainID;
14727  TRunningEntry RunningEntry;
14728  TDateTime StartTime;
14729  AnsiString HeadCode;
14730 */
14731  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
14732  std::ofstream OutFile("TrainData.csv");
14733 
14734  if(OutFile == 0)
14735  {
14736  ShowMessage("Output file TrainData.csv failed to open");
14737  Utilities->CallLogPop(1372);
14738  return;
14739  }
14740  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14741  {
14742  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14743  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
14744 
14745  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
14746  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
14747 
14748  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
14749  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
14750  "RepeatNumber" << '\n' << '\n';
14751  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14752  {
14753  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14754  AnsiString TimetableEntryTypeStr;
14755  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
14756  switch(AVEntry.FormatType)
14757  {
14758  case 0:
14759  {
14760  TimetableEntryTypeStr = "NoFormat";
14761  break;
14762  }
14763 
14764  case 1:
14765  {
14766  TimetableEntryTypeStr = "TimeLoc";
14767  break;
14768  }
14769 
14770  case 2:
14771  {
14772  TimetableEntryTypeStr = "TimeTimeLoc";
14773  break;
14774  }
14775 
14776  case 3:
14777  {
14778  TimetableEntryTypeStr = "TimeCmd";
14779  break;
14780  }
14781 
14782  case 4:
14783  {
14784  TimetableEntryTypeStr = "StartNew";
14785  break;
14786  }
14787 
14788  case 5:
14789  {
14790  TimetableEntryTypeStr = "TimeCmdHeadCode";
14791  break;
14792  }
14793 
14794  case 6:
14795  {
14796  TimetableEntryTypeStr = "FinRemHere";
14797  break;
14798  }
14799 
14800  case 7:
14801  {
14802  TimetableEntryTypeStr = "FNSShuttle";
14803  break;
14804  }
14805 
14806  case 8:
14807  {
14808  TimetableEntryTypeStr = "SNTShuttle";
14809  break;
14810  }
14811 
14812  case 9:
14813  {
14814  TimetableEntryTypeStr = "SNSShuttle";
14815  break;
14816  }
14817 
14818  case 10:
14819  {
14820  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
14821  break;
14822  }
14823 
14824  case 11:
14825  {
14826  TimetableEntryTypeStr = "FSHNewService";
14827  break;
14828  }
14829 
14830  case 12:
14831  {
14832  TimetableEntryTypeStr = "Repeat";
14833  break;
14834  }
14835 
14836  default:
14837  {
14838  TimetableEntryTypeStr = "Default";
14839  break;
14840  }
14841  }
14842  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
14843  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14844  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14845  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14846  AVEntry.NumberOfRepeats << '\n';
14847  }
14848  OutFile << '\n';
14849  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14850  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14851  {
14852  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14853  AnsiString RunningEntryStr;
14854  // NotStarted, Running, Exited
14855  switch(TOD.RunningEntry)
14856  {
14857  case 0:
14858  {
14859  RunningEntryStr = "NotStarted";
14860  break;
14861  }
14862 
14863  case 1:
14864  {
14865  RunningEntryStr = "Running";
14866  break;
14867  }
14868 
14869  case 2:
14870  {
14871  RunningEntryStr = "Exited";
14872  break;
14873  }
14874  }
14875  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14876  }
14877  OutFile << '\n';
14878  }
14879  OutFile.close();
14880  Utilities->CallLogPop(1373);
14881 }
14882 
14883 // ---------------------------------------------------------------------------
14884 
14885 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14886 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14887 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14888 {
14889  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14890  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14892  ShowMessage(Message);
14893  BaseTime = TDateTime::CurrentDateTime();
14894  StopTTClockFlag = false;
14895  Utilities->CallLogPop(1374);
14896 }
14897 
14898 // ---------------------------------------------------------------------------
14899 
14900 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14901 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14902 // from the start of the relevant vectors. Can't save the pointer values
14903 // as these will be different each time the vectors are created
14904 {
14905  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14906  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14907  for(unsigned int x = 0; x < TrainVector.size(); x++)
14908  {
14909  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14910  }
14911  Utilities->CallLogPop(1375);
14912 }
14913 
14914 // ---------------------------------------------------------------------------
14915 
14916 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14917 {
14918  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14919  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14920  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14921  // by zero error in calculating AValue, use 1
14922  for(int x = 0; x < NumberOfTrains; x++)
14923  {
14924  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14925  // by zero error in calculating AValue, use 1
14926  NewTrain->LoadOneSessionTrain(0, SessionFile);
14927  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14928  // added at v2.4.0. have to include as that value not stored in session file
14929  {
14930  NewTrain->StoppedWithoutPower = true;
14931  }
14932  TrainVector.push_back(*NewTrain);
14933  LastTrainLoaded = x;
14934  }
14935  delete NewTrain;
14936  Utilities->CallLogPop(1376);
14937 }
14938 
14939 // ---------------------------------------------------------------------------
14940 
14941 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14942 {
14943  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14944  int NumberOfTrains;
14945 
14946  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14947  {
14948  Utilities->CallLogPop(1377);
14949  return(false);
14950  }
14951  for(int x = 0; x < NumberOfTrains; x++)
14952  {
14953  if(!(TTrain::CheckOneSessionTrain(InFile)))
14954  {
14955  Utilities->CallLogPop(1378);
14956  return(false);
14957  }
14958  }
14959  Utilities->CallLogPop(1379);
14960  return(true);
14961 }
14962 
14963 // ---------------------------------------------------------------------------
14964 
14965 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14966 {
14967  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14968  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14969  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14970  {
14971  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14972  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14973  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14974  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14975  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14976  }
14977  Utilities->CallLogPop(1380);
14978 }
14979 
14980 // ---------------------------------------------------------------------------
14981 
14982 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14983 {
14984  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14985  TAllRoutes::TLockedRouteClass LockedRouteObject;
14986  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14987 
14988  for(int x = 0; x < LockedRouteVectorSize; x++)
14989  {
14990  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14991  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14992  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14993  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14994  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14995  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14996  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14997  }
14998  Utilities->CallLogPop(1381);
14999 }
15000 
15001 // ---------------------------------------------------------------------------
15002 
15003 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15004 {
15005  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15006  int LockedRouteVectorSize;
15007 
15008  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15009  {
15010  Utilities->CallLogPop(1382);
15011  return(false);
15012  }
15013  for(int x = 0; x < LockedRouteVectorSize; x++)
15014  {
15015  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15016  {
15017  Utilities->CallLogPop(1383);
15018  return(false);
15019  }
15020  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15021  {
15022  Utilities->CallLogPop(1384);
15023  return(false);
15024  }
15025  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15026  {
15027  Utilities->CallLogPop(1385);
15028  return(false);
15029  }
15030  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15031  {
15032  Utilities->CallLogPop(1386);
15033  return(false);
15034  }
15035  if(!Utilities->CheckFileDouble(SessionFile))
15036  {
15037  Utilities->CallLogPop(1387);
15038  return(false);
15039  }
15040  }
15041  Utilities->CallLogPop(1388);
15042  return(true);
15043 }
15044 
15045 // ---------------------------------------------------------------------------
15046 
15047 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15048 {
15049  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15050  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15051  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15052  {
15053  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15054  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15055  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15056  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15057  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15058  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15059  }
15060  Utilities->CallLogPop(1389);
15061 }
15062 
15063 // ---------------------------------------------------------------------------
15064 
15065 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15066 {
15067  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15068  TContinuationAutoSigEntry ContinuationAutoSigObject;
15069  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15070 
15071  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15072  {
15073  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15074  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15075  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15076  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15077  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15078  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15079  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15080  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15081  }
15082  Utilities->CallLogPop(1390);
15083 }
15084 
15085 // ---------------------------------------------------------------------------
15086 
15087 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15088 {
15089  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15090  int ContinuationAutoSigVectorSize;
15091 
15092  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15093  {
15094  Utilities->CallLogPop(1391);
15095  return(false);
15096  }
15097  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15098  {
15099  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15100  {
15101  Utilities->CallLogPop(1392);
15102  return(false);
15103  }
15104  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15105  {
15106  Utilities->CallLogPop(1393);
15107  return(false);
15108  }
15109  if(!Utilities->CheckFileDouble(SessionFile))
15110  {
15111  Utilities->CallLogPop(1405);
15112  return(false);
15113  }
15114  if(!Utilities->CheckFileDouble(SessionFile))
15115  {
15116  Utilities->CallLogPop(1406);
15117  return(false);
15118  }
15119  if(!Utilities->CheckFileDouble(SessionFile))
15120  {
15121  Utilities->CallLogPop(1407);
15122  return(false);
15123  }
15124  if(!Utilities->CheckFileDouble(SessionFile))
15125  {
15126  Utilities->CallLogPop(1394);
15127  return(false);
15128  }
15129  }
15130  Utilities->CallLogPop(1395);
15131  return(true);
15132 }
15133 
15134 // ---------------------------------------------------------------------------
15135 
15136 /*
15137  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15138  {
15139  public:
15140  AnsiString Description; ///< service description
15141  AnsiString HeadCode; ///< service headcode
15142  int RepeatNumber; ///< service RepeatNumber
15143  int IncrementalMinutes; ///< Repeat separation in minutes
15144  int IncrementalDigits; ///< Repeat headcode separation
15145  int VectorPosition; ///< TrackVectorPosition for the continuation element
15146  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15147  };
15148 
15149 
15150  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15151  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15152 */
15153 
15155 // build this into timetable load so session loading can use it too
15156 // being a multimap it automatically sorts in ascending EventTime order
15157 {
15158  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15160  // need to clear as this called twice when load a session
15161  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15162  {
15163  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15164  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15165  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15166 
15167  if(AVFirstEntry.Command == "Snt")
15168  // new train (no need to include Snt-sh since they can't start at a continuation)
15169  {
15172  {
15174  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15175  // retains this value for all repeats
15176  CTEEntry.RepeatNumber = 0; // for first entry
15177  CTEEntry.TrainDataEntryPtr = &TDEntry;
15178  // retains this value for all repeats
15179  CTEEntry.HeadCode = TDEntry.HeadCode;
15180  CTEEntry.Description = TDEntry.Description;
15181  CTEEntry.IncrementalMinutes = 0;
15182  CTEEntry.IncrementalDigits = 0;
15183  if(AVLastEntry.FormatType == Repeat)
15184  {
15185  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15186  // retains this value or 0 for all repeats
15187  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15188  // retains this value or 0 for all repeats
15189  }
15190  CTEMMP.first = AVFirstEntry.EventTime;
15191  CTEMMP.second = CTEEntry;
15192  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15193  // base entry
15194  if(TDEntry.NumberOfTrains > 1)
15195  {
15196  if(AVLastEntry.FormatType != Repeat)
15197  {
15198  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15199  }
15200  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15201  {
15202  CTEEntry.RepeatNumber = y;
15203  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15204  // CTEEntry.VectorPosition stays same
15205  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15206  CTEMMP.second = CTEEntry;
15207  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15208  }
15209  }
15210  }
15211  }
15212  }
15213  Utilities->CallLogPop(1396);
15214 }
15215 
15216 // ---------------------------------------------------------------------------
15217 
15219 {
15220  // called when WarningFlashCount == 0 or when press zoomout button
15221  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15222  if(!Display->ZoomOutFlag)
15223  {
15224  Utilities->CallLogPop(1156);
15225  return;
15226  }
15227  for(unsigned int x = 0; x < TrainVector.size(); x++)
15228  {
15229  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15230  // if OldPlotElement[x] == -1 then ignore (not plotted)
15232  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15233  }
15234  Display->Update();
15235  // need to keep this since Update() not called for PlotSmallOutput as too slow
15236  Utilities->CallLogPop(742);
15237 }
15238 
15239 // ---------------------------------------------------------------------------
15240 
15241 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15242 {
15243  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15244  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15245  {
15246  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15247  }
15248  Utilities->CallLogPop(740);
15249  return(TrainVector.at(VecPos));
15250 }
15251 
15252 // ---------------------------------------------------------------------------
15253 
15254 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15255 {
15256  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15257  AnsiString RetStr = "", PartStr = "";
15258 
15259 
15260 /*
15261  Have description & mass etc for train at top - header, then array of actions
15262 
15263  class TActionVectorEntry
15264  {
15265  public:
15266  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15268  bool SignallerControl;
15270  bool Warning;
15272  int NumberOfRepeats;
15274  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15276  TDateTime EventTime, ArrivalTime, DepartureTime;
15278  TExitList ExitList;
15280  TTimetableFormatType FormatType;
15282  TTimetableLocationType LocationType;
15284  TTimetableSequenceType SequenceType;
15286  TTimetableShuttleLinkType ShuttleLinkType;
15288  TTrainDataEntry *LinkedTrainEntryPtr;
15290  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15292 
15293  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15294 
15295  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15296 
15297  class TTrainOperatingData
15298  {
15299  public:
15300  int TrainID;
15301  TActionEventType EventReported;
15302  TRunningEntry RunningEntry;
15303 
15304  //inline function
15305  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15306  };
15307 
15308  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15309 
15310  class TTrainDataEntry
15311  {
15312  public:
15313  AnsiString HeadCode, ServiceReference, Description;
15315  double MaxBrakeRate;
15317  double MaxRunningSpeed;
15319  double PowerAtRail;
15321  int Mass;
15323  int NumberOfTrains;
15325  int SignallerSpeed;
15327  int StartSpeed;
15329  TActionVector ActionVector;
15331  TTrainOperatingDataVector TrainOperatingDataVector;
15333 
15334  //inline function
15335  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15336  };
15337 
15338  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15339 
15340  //formatted timetable types
15341  class TOneTrainFormattedEntry
15342  {
15343  AnsiString Action;//includes location if relevanr
15344  AnsiString Time;
15345  };
15346 
15347  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15348 
15349  class TOneCompleteFormattedTrain//headcode + list of actions
15350  {
15351  public:
15352  AnsiString HeadCode;
15353  TOneFormattedTrainVector OneFormattedTrainVector;
15354  };
15355 
15356  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15357 
15358  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15359  {
15360  public:
15361  AnsiString Header;//description, mass, power, brake rate etc
15362  int NumberOfTrains;// number of repeats + 1
15363  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15364  };
15365 
15366 
15367  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15368  //end of formatted timetable types
15369 
15370 */
15371 
15372  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15373 
15374  // format "16/06/2009 20:55:17"
15375  // avoid characters in filename:= / \ : * ? " < > |
15376  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15377 
15378  AnsiString ShortTTName = "";
15379 
15380  for(int x = TTFileName.Length(); x > 0; x--)
15381  {
15382  if(TTFileName[x] == '\\')
15383  {
15384  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15385  break;
15386  }
15387  }
15388 
15389  ShowMessage("Creates two timetables named " + ShortTTName +
15390  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15391 
15392  Screen->Cursor = TCursor(-11); // Hourglass
15393 
15394  AnsiString FormatNoDPStr = "#######0";
15395  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15396 
15398  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15399  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15400 
15401  // all timetable in formatted form
15402  //create the AllTTTrains vector
15403  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15404  {
15405  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15406  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15407  if(TrainDataEntry.Mass > 0)
15408  {
15409  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15410  }
15411  if(TrainDataEntry.PowerAtRail > 0)
15412  {
15413  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15414  }
15415  if(TrainDataEntry.MaxBrakeRate > 0)
15416  {
15417  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15418  }
15419  if(TrainDataEntry.MaxRunningSpeed > 0)
15420  {
15421  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15422  }
15423  FirstHeadCode = TrainDataEntry.HeadCode;
15424  int IncDigits = 0, IncMinutes = 0;
15425  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15426  if(!ActionVector.empty())
15427  {
15428  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15429  {
15430  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15431  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15432  }
15433  }
15434  TTrainFormattedInformation OneTTLine;
15435  // contains all information for a single TT entry (including repeats)
15436  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15437  {
15438  OneTTLine.Header = "";
15439  if((TrainDataEntry.Description != "") && (MassStr != ""))
15440  {
15441  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15442  }
15443  else if(TrainDataEntry.Description != "")
15444  {
15445  OneTTLine.Header = TrainDataEntry.Description;
15446  }
15447  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15448  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15449  for(unsigned int z = 0; z < ActionVector.size(); z++)
15450  {
15451  TOneTrainFormattedEntry OneTTEntry;
15452  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15453  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15454  AnsiString PartStr = "", TimeStr = "";
15455 /*
15456  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15457  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15458  ExitRailway};
15459  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15460  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15461  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15462 */
15463  if(ActionVectorEntry.SequenceType == Start)
15464  {
15465  if(ActionVectorEntry.FormatType == StartNew)
15466  {
15467  if(ActionVectorEntry.LocationName != "")
15468  {
15469  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15470  {
15471  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15472  }
15473  else
15474  {
15475  PartStr = "Created at " + ActionVectorEntry.LocationName;
15476  }
15477  }
15478  else // may be a named continuation or other element, and if so report that
15479  {
15480  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15481  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15482  {
15483  if(LocName != "")
15484  {
15485  PartStr = "Enters at " + LocName;
15486  }
15487  else // use rear position if it's a continuation
15488  {
15489  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15490  }
15491  }
15492  else // not a continuation
15493  {
15494  if(LocName != "")
15495  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15496  // but include anyway
15497  {
15498  PartStr = "Created at " + LocName;
15499  }
15500  else // use rear position again
15501  {
15502  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15503  }
15504  }
15505  }
15506  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15507  }
15508  else if(ActionVectorEntry.FormatType == SNTShuttle)
15509  {
15510  if(y == 0) // first train
15511  {
15512  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15513  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15514  }
15515  else
15516  {
15517  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15518  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15519  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15520  } // y-1 for headcode above since it is the last repeat value that the train is from
15521 
15522  }
15523  else if(ActionVectorEntry.Command == "Sfs")
15524  {
15525  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15526  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15527  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15528  }
15529  else if(ActionVectorEntry.Command == "Sns")
15530  {
15531  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15532  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15533  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15534  }
15535  else if(ActionVectorEntry.FormatType == SNSShuttle)
15536  {
15537  if(y == 0) // first entry from shuttle
15538  {
15539  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15540  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15541  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15542  }
15543  else
15544  {
15545  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15546  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15547  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15548  } // y-1 for headcode above since it is the last repeat value that the train is from
15549 
15550  }
15551  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15552  {
15553  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15554  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15555  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15556  AnsiString FirstHeadCode = TDE->HeadCode;
15557  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15558  // a shuttle has to have at least 1 repeat
15559  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15560  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15561  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15562  }
15563  }
15564  else if(ActionVectorEntry.SequenceType == Intermediate)
15565  {
15566  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15567  {
15568  // here need 2 entries if times different so push the first right away & the second later
15569  // if times same just give the arrival entry
15570  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15571  {
15572  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15573  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15574  OneTTEntry.Action = PartStr;
15575  OneTTEntry.Time = TimeStr;
15576  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15577  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15578  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15579  }
15580  else
15581  {
15582  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15583  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15584  }
15585  }
15586  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15587  {
15588  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15589  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15590  }
15591  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15592  {
15593  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15594  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15595  }
15596  else if(ActionVectorEntry.FormatType == PassTime)
15597  {
15598  PartStr = "Passes " + ActionVectorEntry.LocationName;
15599  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15600  }
15601  else if(ActionVectorEntry.Command == "jbo")
15602  {
15603  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15604  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15605  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15606  }
15607  else if(ActionVectorEntry.Command == "fsp")
15608  {
15609  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15610  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15611  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15612  }
15613  else if(ActionVectorEntry.Command == "rsp")
15614  {
15615  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15616  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15617  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15618  }
15619  else if(ActionVectorEntry.Command == "cdt")
15620  {
15621  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15622  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15623  }
15624  }
15625  else if(ActionVectorEntry.SequenceType == Finish)
15626  {
15627  if(ActionVectorEntry.Command == "Fns")
15628  {
15629  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15630  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15631  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15632  }
15633  else if(ActionVectorEntry.Command == "F-nshs")
15634  {
15635  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15636  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15637  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15638  }
15639  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15640  {
15641  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15642  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15643  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15644  // y+1 because it's the NEXT service repeat number that is relevant
15645  }
15646  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15647  {
15648  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15649  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15650  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15651  }
15652  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15653  {
15654  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15655  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15656  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15657  // y+1 because it's the NEXT service repeat number that is relevant
15658  }
15659  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15660  {
15661  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15662  // only used in chronological tt
15663  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15664  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15665  }
15666  else if(ActionVectorEntry.Command == "Frh")
15667  {
15668  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15669  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15670  if(z > 0)
15671  // should be for finish entry but include check for safety
15672  {
15673  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15674  {
15675  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15676  }
15677  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15678  {
15679  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15680  }
15681  else
15682  {
15683  TimeStr = " "; // shouldn't ever get here
15684  }
15685  }
15686  }
15687  else if(ActionVectorEntry.Command == "Fer")
15688  {
15689  AnsiString AllowedExits;
15690  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15691  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15692  }
15693  else if(ActionVectorEntry.Command == "Fjo")
15694  {
15695  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15696  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15697  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15698  }
15699  }
15700  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15701  {
15702  continue; // no entry needed for a repeat
15703  }
15704  OneTTEntry.Action = PartStr;
15705  OneTTEntry.Time = TimeStr;
15706  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15707  // one per action
15708  }
15709  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15710  // one per repeat
15711  }
15712  AllTTTrains->push_back(OneTTLine); // one per repeating train
15713  }
15714  // AllTTTrains vector now complete
15715 
15716  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15717 
15718  if(TTFile == 0)
15719  {
15720  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
15721  delete AllTTTrains;
15722  Utilities->CallLogPop(1567);
15723  return;
15724  }
15725 /* formatted timetable types
15726  class TOneTrainFormattedEntry
15727  {
15728  AnsiString Action;//includes location if relevant
15729  AnsiString Time;
15730  };
15731 
15732  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15733 
15734  class TOneCompleteFormattedTrain//headcode + list of actions
15735  {
15736  public:
15737  AnsiString HeadCode;
15738  TOneFormattedTrainVector OneFormattedTrainVector;
15739  };
15740 
15741  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15742 
15743  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15744  {
15745  public:
15746  AnsiString Header;//description, mass, power, brake rate etc
15747  int NumberOfTrains;// number of repeats + 1
15748  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15749  };
15750 
15751  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15752  //end of formatted timetable types
15753 */
15754 
15755  // new layout using multiple rows
15756  TTFile << TableTitle.c_str() << '\n' << '\n';
15757  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15758  {
15759  TTFile << AllTTTrains->at(x).Header.c_str();
15760  TTFile << '\n';
15761  TTFile << ','; // for the blank line above the action list
15762  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15763  {
15764  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15765  {
15766  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
15767  }
15768  else
15769  {
15770  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
15771  }
15772  }
15773  TTFile << '\n' << '\n';
15774 
15775  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
15776  {
15777  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
15778  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15779  {
15780  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15781  {
15782  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
15783  }
15784  else
15785  {
15786  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
15787  }
15788  }
15789  TTFile << '\n';
15790  }
15791  TTFile << '\n' << '\n';
15792  }
15793 
15794  TTFile.close();
15795 
15796  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15797 
15798  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15799 
15800  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
15801 
15802  if(TTFile2 == 0)
15803  {
15804  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
15805  delete AllTTTrains;
15806  Utilities->CallLogPop(1710);
15807  return;
15808  }
15809  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
15810  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
15811  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
15812 
15813  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
15814  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
15815 
15816  // multimap of AnsiStrings with TimeString as key (to sort automatically)
15817 
15818  TTFile2 << TableTitle.c_str() << '\n' << '\n';
15819  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15820  {
15821  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15822  {
15823  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
15824  {
15825  bool GiveMessagesFalse = false;
15826  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
15827  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
15828  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
15829  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
15830  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
15831  {
15832  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
15833  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
15834  TimeString = TimeString.SubString(9, 5);
15835  ActionString += " " + OtherHeadCode;
15836  }
15837  if(TimeString.SubString(1, 7) == "End at ")
15838  // for Frh-sh final entry
15839  {
15840  TimeString = TimeString.SubString(8, 5);
15841  }
15842  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
15843  AnsiMultiMapEntry.first = TimeString;
15844  AnsiMultiMapEntry.second = OneLine;
15845  TAMM->insert(AnsiMultiMapEntry);
15846  }
15847  }
15848  }
15849 
15850  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
15851  {
15852  TTFile2 << (AMMIT->second).c_str();
15853  }
15854  delete AllTTTrains;
15855  delete TAMM;
15856  TTFile2.close();
15857  Utilities->CallLogPop(1580);
15858 }
15859 
15860 // ---------------------------------------------------------------------------
15861 
15862 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15863  bool AtLocChecked, int ArrRange, int DepRange)
15864 {
15865 
15866  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15867  bool AnalysisError = false;
15868 
15869  try
15870  {
15871  //New section at v2.5.0 for tt conflict analysis
15872  /*
15873  typedef std::list<AnsiString> TServiceCallingLocsList;
15874  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
15875 
15877  struct TLocServiceTimes
15878  {
15879  AnsiString Location;
15880  AnsiString ServiceAndRepeatNum;
15881  AnsiString AtLocTime;
15882  AnsiString ArrTime;
15883  AnsiString DepTime;
15884  AnsiString FrhMarker;
15885  };
15886  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15887  */
15888 
15889  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15890  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15891 
15892  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15893  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15894  int Suffix = 0;
15895  int IteratorNumber = 0;
15896  AnsiString AnsiSuffix = "";
15897  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15898  {
15899  IteratorNumber++; //first value in loop is 1
15900  Suffix = 0;
15901  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15902  {
15903  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15904  {
15905  Suffix++; //first value is 1
15906  AnsiSuffix = AnsiString(Suffix);
15907  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15908  }
15909  }
15910  }
15911 
15912  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15913  TServiceCallingLocsList ServiceCallingLocsList;
15914  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15915  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15916  {
15917  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15918  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15919  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15920  ServiceCallingLocsList.clear();
15921  if(ActionVector.empty())
15922  {
15923  continue;
15924  }
15925  if(ActionVector.at(0).SignallerControl)
15926  {
15927  continue;
15928  }
15929  for(unsigned int z = 0; z < ActionVector.size(); z++)
15930  {
15931  TActionVectorEntry AVE = ActionVector.at(z);
15932  if(AVE.FormatType == StartNew)
15933  {
15934  if(AVE.LocationType == AtLocation) //located Snt
15935  {
15936  ServiceCallingLocsList.push_back(AVE.LocationName);
15937  }
15938  else //unlocated Snt (could be entering at continuation)
15939  {
15941  if(TE.ActiveTrackElementName != "")
15942  {
15943  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15944  }
15945  else
15946  {
15947  int HLoc = TE.HLoc;
15948  int VLoc = TE.VLoc;
15949  AnsiString HString;
15950  AnsiString VString;
15951  if(HLoc < 0)
15952  {
15953  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15954  }
15955  else
15956  {
15957  HString = AnsiString(HLoc);
15958  }
15959  if(VLoc < 0)
15960  {
15961  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15962  }
15963  else
15964  {
15965  VString = AnsiString(VLoc);
15966  }
15967  ServiceCallingLocsList.push_back(HString + '-' + VString);
15968  }
15969  }
15970  }
15971  else if(AVE.SequenceType == Start) //other start entries, all located
15972  {
15973  ServiceCallingLocsList.push_back(AVE.LocationName);
15974  }
15975  else if(AVE.FormatType == TimeLoc) //z must be > 0
15976  {
15977  if(ServiceCallingLocsList.back() != AVE.LocationName)
15978  {
15979  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15980  }
15981  }
15982  else if(AVE.FormatType == PassTime)
15983  {
15984  ServiceCallingLocsList.push_back(AVE.LocationName);
15985  }
15986  else if(AVE.FormatType == TimeTimeLoc)
15987  {
15988  ServiceCallingLocsList.push_back(AVE.LocationName);
15989  }
15990  else if(AVE.Command == "cdt") //list if not next to start or finish
15991  {
15992  if(ActionVector.at(z-1).SequenceType == Start)
15993  {
15994  continue;
15995  }
15996  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15997  {
15998  continue;
15999  }
16000  else
16001  {
16002  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16003  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16004  }
16005  }
16006  else if(AVE.FormatType == ExitRailway) //Fer
16007  {
16008  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16009  AnsiString LName = TE.ActiveTrackElementName;
16010  if(LName != "")
16011  {
16012  ServiceCallingLocsList.push_back(LName);
16013  }
16014  else
16015  {
16016  int HLoc = TE.HLoc;
16017  int VLoc = TE.VLoc;
16018  AnsiString HString;
16019  AnsiString VString;
16020  if(HLoc < 0)
16021  {
16022  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16023  }
16024  else
16025  {
16026  HString = AnsiString(HLoc);
16027  }
16028  if(VLoc < 0)
16029  {
16030  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16031  }
16032  else
16033  {
16034  VString = AnsiString(VLoc);
16035  }
16036  ServiceCallingLocsList.push_back(HString + '-' + VString);
16037  }
16038  }
16039  }
16040  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16041  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16042  }
16043  //AllServiceCallingLocsMap built
16044 
16045  //test validity of AllServiceCallingLocsMap
16046 /*
16047  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16048  std::ofstream Test(TestFile.c_str());
16049 
16050  if(TestFile == 0)
16051  {
16052  ShowMessage("TestFile failed to open - can't be created");
16053  Utilities->CallLogPop();
16054  return false;
16055  }
16056 
16057  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16058  {
16059  Test << ASCLIt->first << '\n'; //service ref
16060  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16061  {
16062  Test << *SCLIt << '\n';
16063  }
16064  Test << "\n\n";
16065  }
16066  Test.close();
16067  Utilities->CallLogPop();
16068  return true;
16069 */
16070 
16071  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16072  if(TrainDataVector.empty())
16073  {
16074  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16075  Utilities->CallLogPop(2209);
16076  return(false);
16077  }
16078  TLocServiceTimes TLSTEntry;
16079  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16080  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16081  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16082  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16083  LastTTTime = "";
16084 
16085  //calculate LastTTTime
16086  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16087  {
16088  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16089  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16090  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16091  TDateTime LastTDTime;
16092  int IncMinutes = 0;
16093  NumTrains = TrainDataEntry.NumberOfTrains;
16094  if(ActionVector.empty())
16095  {
16096  continue;
16097  }
16098  if(ActionVector.at(0).SignallerControl)
16099  {
16100  continue;
16101  }
16102  if(AVLast->FormatType == Repeat)
16103  {
16104  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16105  AVLast--; //now points to the command before the repeat
16106  }
16107  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16108  {
16109  AVLast--; //points to last timed entry
16110  }
16111  //here AVLast points to last entry with a time
16112  if(AVLast->ArrivalTime != TDateTime(-1))
16113  {
16114  LastTDTime = AVLast->ArrivalTime;
16115  }
16116  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16117  {
16118  LastTDTime = AVLast->EventTime;
16119  }
16120  else
16121  {
16122  continue; //shouldn't ever reach here but if do then skip this service
16123  }
16124  if(NumTrains == 1)
16125  {
16126  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16127  }
16128  else
16129  {
16130  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16131  }
16132  if(LastAnsiTime > LastTTTime)
16133  {
16134  LastTTTime = LastAnsiTime;
16135  }
16136  }
16137 
16138 //build LocServiceTimesVector
16139 
16140 /*
16141  struct TLocServiceTimes
16142  {
16143  AnsiString Location;
16144  AnsiString ServiceAndRepeatNum;
16145  AnsiString AtLocTime;
16146  AnsiString ArrTime;
16147  AnsiString DepTime;
16148  AnsiString FrhMarker;
16149  };
16150  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16151 
16152 This works as follows:
16153 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16154 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16155 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16156 
16157 Every action for every train is examined and times entered as follows:-
16158 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16159 b) an unlocated Snt: entry time becomes DepTime
16160 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16161 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16162 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16163 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16164 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16165 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16166 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16167 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16168 */
16169  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16170  {
16171  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16172  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16173  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16174  int IncMinutes = 0;
16175  NumTrains = TrainDataEntry.NumberOfTrains;
16176  if(ActionVector.empty())
16177  {
16178  continue;
16179  }
16180  if(ActionVector.at(0).SignallerControl)
16181  {
16182  continue;
16183  }
16184  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16185  {
16186  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16187  }
16188  for(int y = 0; y < NumTrains; y++)
16189  {
16190  if(NumTrains == 1)
16191  {
16192  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16193  }
16194  else if(y == 0)
16195  {
16196  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16197  }
16198  else
16199  {
16200  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16201  }
16202  for(unsigned int z = 0; z < ActionVector.size(); z++)
16203  {
16204  TActionVectorEntry AVE = ActionVector.at(z);
16205  TLSTEntry.AtLocTime = "";
16206  TLSTEntry.ArrTime = "";
16207  TLSTEntry.DepTime = "";
16208  TLSTEntry.Location = "";
16209  TLSTEntry.FrhMarker = "";
16210 
16211  if(AVE.FormatType == StartNew) //Snt only
16212  {
16213  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16214  {
16215  TLSTEntry.Location = AVE.LocationName;
16216  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16217  LocServiceTimesVector.push_back(TLSTEntry);
16218 
16219  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16220  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16221  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16222  {
16223  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16224  {
16225  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16226  break;
16227  }
16228  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16229  {
16230  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16231  break;
16232  }
16233  }
16234  if(FoundStopTime == "")
16235  {
16236  throw Exception("Failure to determine FoundStopTime for located Snt");
16237  }
16238  int WhileCount = 0;
16239  while(true)
16240  {
16241  //add minutes until reach FoundStopTime but don't add that time
16242  WhileCount++;
16243  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16244  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16245  TLSTEntry.DepTime = "";
16246  TLSTEntry.ArrTime = "";
16247  if(IncTime >= FoundStopTime) //don't add that time
16248  {
16249  break;
16250  }
16251  LocServiceTimesVector.push_back(TLSTEntry);
16252  if(WhileCount > 2000)
16253  {
16254  throw Exception("While loop failed to break in 2000 loops for located Snt");
16255  }
16256  }
16257  }
16258  else //unlocated Snt, use the EventTime as DepTime for this vector
16259  {
16261  if(TE.ActiveTrackElementName != "")
16262  {
16263  TLSTEntry.Location = TE.ActiveTrackElementName;
16264  }
16265  else
16266  {
16267  int HLoc = TE.HLoc;
16268  int VLoc = TE.VLoc;
16269  AnsiString HString;
16270  AnsiString VString;
16271  if(HLoc < 0)
16272  {
16273  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16274  }
16275  else
16276  {
16277  HString = AnsiString(HLoc);
16278  }
16279  if(VLoc < 0)
16280  {
16281  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16282  }
16283  else
16284  {
16285  VString = AnsiString(VLoc);
16286  }
16287  TLSTEntry.Location = HString + '-' + VString;
16288  }
16289  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16290  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16291  LocServiceTimesVector.push_back(TLSTEntry);
16292  }
16293  }
16294 
16295  else if(AVE.SequenceType == Start) //other start entries, all located
16296  {
16297  TLSTEntry.Location = AVE.LocationName;
16298  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16299  LocServiceTimesVector.push_back(TLSTEntry);
16300  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16301  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16302  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16303  {
16304  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16305  {
16306  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16307  break;
16308  }
16309  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16310  {
16311  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16312  break;
16313  }
16314  }
16315  if(FoundStopTime == "")
16316  {
16317  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16318  }
16319  int WhileCount = 0;
16320  while(true)
16321  {
16322  //add minutes until reach FoundStopTime but don't add that time
16323  WhileCount++;
16324  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16325  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16326  TLSTEntry.DepTime = "";
16327  TLSTEntry.ArrTime = "";
16328  if(IncTime >= FoundStopTime) //don't add that time
16329  {
16330  break;
16331  }
16332  LocServiceTimesVector.push_back(TLSTEntry);
16333  if(WhileCount > 2000)
16334  {
16335  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16336  }
16337  }
16338  }
16339 
16340  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16341  {
16342  TLSTEntry.Location = AVE.LocationName;
16343  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16344  {
16345  bool SkipAddingMinutes = false;
16346  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16347  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16348  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16349  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16350  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16351  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16352  {
16353  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16354  {
16355  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16356  break;
16357  }
16358  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16359  {
16360  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16361  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16362  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16363  {
16364  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16365  SkipAddingMinutes = true;
16366  }
16367  break;
16368  }
16369  }
16370  if(FoundStopTime == "")
16371  {
16372  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16373  }
16374  if(!SkipAddingMinutes)
16375  {
16376  int WhileCount = 0;
16377  while(true)
16378  {
16379  //add minutes until reach FoundStopTime but don't add that time
16380  WhileCount++;
16381  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16382  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16383  TLSTEntry.DepTime = "";
16384  TLSTEntry.ArrTime = "";
16385  if(IncTime >= FoundStopTime) //don't add that time
16386  {
16387  break;
16388  }
16389  LocServiceTimesVector.push_back(TLSTEntry);
16390  if(WhileCount > 2000)
16391  {
16392  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16393  }
16394  }
16395  }
16396  }
16397  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16398  {
16399  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16400  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16401  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16402  {
16403  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16404  {
16405  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16406  LocServiceTimesVector.pop_back();
16407  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16408  }
16409  else //just add the dep & atloc times
16410  {
16411  TLSTEntry.ArrTime = "";
16412  LocServiceTimesVector.push_back(TLSTEntry);
16413  }
16414  }
16415  else //just add the dep & atloc times
16416  {
16417  TLSTEntry.ArrTime = "";
16418  LocServiceTimesVector.push_back(TLSTEntry);
16419  }
16420  }
16421  }
16422 
16423  else if(AVE.FormatType == TimeTimeLoc)
16424  {
16425  TLSTEntry.Location = AVE.LocationName;
16426  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16427  {
16428  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16429  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16430  }
16431  if(AVE.DepartureTime > TDateTime(-1)) //should be
16432  {
16433  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16434  }
16435  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16436  {
16437  LocServiceTimesVector.push_back(TLSTEntry);
16438  }
16439  else
16440  {
16441  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16442  TLSTEntry.DepTime = "";
16443  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16444  TLSTEntry.ArrTime = ""; //done with this now
16445  while(TLSTEntry.AtLocTime < TempDepTime)
16446  {
16447  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16448  if(TLSTEntry.AtLocTime == TempDepTime)
16449  {
16450  TLSTEntry.DepTime = TempDepTime; //restore value
16451  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16452  }
16453  else
16454  {
16455  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16456  }
16457  }
16458  }
16459  }
16460 
16461  else if(AVE.FormatType == PassTime) //added at v2.9.1
16462  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16463  TLSTEntry.Location = AVE.LocationName;;
16464  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16465  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16466  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16467  TLSTEntry.ArrTime = ""; //need to reset this to null
16468  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16469  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16470  }
16471 
16472  else if(AVE.FormatType == ExitRailway) //Fer
16473  {
16474  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16475  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16476  //be wrong, but can't guess from here & most will have same name
16477  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16478  if(LName != "")
16479  {
16480  TLSTEntry.Location = LName;
16481  }
16482  else
16483  {
16484  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16485  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16486  AnsiString HString;
16487  AnsiString VString;
16488  if(HLoc < 0)
16489  {
16490  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16491  }
16492  else
16493  {
16494  HString = AnsiString(HLoc);
16495  }
16496  if(VLoc < 0)
16497  {
16498  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16499  }
16500  else
16501  {
16502  VString = AnsiString(VLoc);
16503  }
16504  TLSTEntry.Location = HString + '-' + VString;
16505  }
16506  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16507  }
16508 
16509  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16510  {
16511  AnsiString FrhTime;
16512  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16513  {
16514  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16515  }
16516  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16517  {
16518  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16519  }
16520  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16521  TLSTEntry.Location = AVE.LocationName;
16522  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16523  TLSTEntry.FrhMarker = "Frh";
16524  LocServiceTimesVector.push_back(TLSTEntry);
16525  TLSTEntry.FrhMarker = "";
16526  //add all times from next minute to end of timetable
16527  while(IncTime <= LastTTTime)
16528  {
16529  TLSTEntry.AtLocTime = IncTime;
16530  LocServiceTimesVector.push_back(TLSTEntry);
16531  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16532  }
16533  }
16534 
16535  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16536  {
16537  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16538  {
16539  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16540  TLSTEntry.Location = AVE.LocationName;
16541  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16542  TLSTEntry.FrhMarker = "Frh";
16543  LocServiceTimesVector.push_back(TLSTEntry);
16544  TLSTEntry.FrhMarker = "";
16545  //add all times from next minute to end of timetable
16546  while(IncTime <= LastTTTime)
16547  {
16548  TLSTEntry.AtLocTime = IncTime;
16549  LocServiceTimesVector.push_back(TLSTEntry);
16550  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16551  }
16552  }
16553  }
16554 
16555  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16556  {
16557  //nothing is done here as the entry will be listed at this time under the new service reference
16558  }
16559  }
16560  }
16561  }
16562 
16563  //now sort in location order
16564  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16565  //LocServiceTimesVector now complete & sorted in location order
16566 
16568 /*
16569  std::ofstream LSTVFile("LSTVFile.txt");
16570  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
16571  {
16572  LSTVFile << LSTVIt->Location + '\n';
16573  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
16574  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
16575  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
16576  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
16577  if(LSTVIt->FrhMarker == "")
16578  {
16579  LSTVFile << "Not Frh\n";
16580  }
16581  else
16582  {
16583  LSTVFile << LSTVIt->FrhMarker + '\n';
16584  }
16585  LSTVFile << '\n';
16586  }
16587  LSTVFile.close();
16588 */
16589  //declare pointers for use in printouts
16590  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16591 
16592  //set up the output file
16593  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16594  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16595 
16596  std::ofstream TTFile3(TTFileName3.c_str());
16597 
16598  if(TTFile3 == 0)
16599  {
16600  ShowMessage("Conflict Analysis file failed to open - can't be created");
16601  Utilities->CallLogPop(2210);
16602  return(false);
16603  }
16604  if(LocServiceTimesVector.empty())
16605  {
16606  ShowMessage("No timetabled services found");
16607  TTFile3.close();
16608  DeleteFile(TTFileName3);
16609  Utilities->CallLogPop(2211);
16610  return(false);
16611  }
16612  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
16613 
16614 
16615  //arrivals
16616  if(ArrChecked)
16617  {
16618  //sort in ArrTime order for each location
16619  Ptr1 = LocServiceTimesVector.begin();
16620  Ptr2 = Ptr1 + 1;
16621  while(Ptr2 != LocServiceTimesVector.end())
16622  {
16623  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16624  {
16625  Ptr2++;
16626  if(Ptr2 == LocServiceTimesVector.end())
16627  {
16628  break;
16629  }
16630  }
16631  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16632  Ptr1 = Ptr2; //first entry with next name
16633  if(Ptr2 != LocServiceTimesVector.end())
16634  {
16635  Ptr2++;
16636  }
16637  }
16638 
16639  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16640 
16641  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
16642  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16643  MinuteString = " minutes";
16644  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16645  if(ArrRange == 1)
16646  {
16647  MinuteString = " minute";
16648  }
16649  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16650  TTFile3 << ",Platforms,Trains\n\n";
16651 
16652  Ptr1 = LocServiceTimesVector.begin();
16653  Ptr2 = Ptr1 + 1;
16654  while(Ptr2 != LocServiceTimesVector.end())
16655  {
16656  PreviousService = "";
16657  NumTrainsAtLoc = 0;
16658  ServiceAndRepeatNumTotal = "";
16659  NumPlats = 0;
16660  NumPlatsAtThisLocCalculated = false;
16661  BasicTime = "";
16662  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16663  {
16664  PreviousService = "";
16665  NumTrainsAtLoc = 0;
16666  ServiceAndRepeatNumTotal = "";
16667  NumPlats = 0;
16668  NumPlatsAtThisLocCalculated = false;
16669  BasicTime = "";
16670  Ptr1++;
16671  Ptr2++;
16672  if(Ptr2 == LocServiceTimesVector.end())
16673  {
16674  break;
16675  }
16676  }
16677  if(Ptr2 == LocServiceTimesVector.end())
16678  {
16679  break;
16680  }
16681  while(Ptr2->Location == Ptr1->Location)
16682  {
16683  PreviousService = "";
16684  NumTrainsAtLoc = 0;
16685  ServiceAndRepeatNumTotal = "";
16686  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16687  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16688  {
16689  break;
16690  }
16691  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
16692  {
16693  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
16694  Ptr1++;
16695  Ptr2++;
16696  if(Ptr2 == LocServiceTimesVector.end())
16697  {
16698  break;
16699  }
16700  if(Ptr2->Location != Ptr1->Location)
16701  {
16702  break;
16703  }
16704  }
16705  if(Ptr2 == LocServiceTimesVector.end())
16706  {
16707  break;
16708  }
16709  if(Ptr2->Location != Ptr1->Location)
16710  {
16711  break;
16712  }
16713  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
16714  {
16715  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
16716  {
16717  break;
16718  }
16719  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16720  {
16721  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
16722  NumPlatsAtThisLocCalculated = true;
16723  }
16724  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16725  {
16726  if(ServiceAndRepeatNumTotal == "")
16727  {
16728  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16729  NumTrainsAtLoc = 1;
16730  }
16731  else
16732  {
16733  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16734  }
16735  }
16736  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16737  if(ServiceAndRepeatNumTotal == "")
16738  {
16739  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16740  NumTrainsAtLoc = 1;
16741  }
16742  else
16743  {
16744  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16745  }
16746  Ptr1 = Ptr2;
16747  Ptr2++;
16748  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
16749  {
16750  int MaxNumberOfSameDirections = 0;
16751  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16752  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16753  {
16754 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16755  TTFile3.close();
16756  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16757 // Utilities->CallLogPop(2224);
16758 // return false;
16759  }
16760  AnsiString Asterisk = "";
16761  if(MaxNumberOfSameDirections >= NumPlats)
16762  {
16763  Asterisk = "* ";
16764  }
16765  //print out a single line for number of trains at loc with all service refs
16766  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16767  ArrivalsPrinted = true;
16768  ServiceAndRepeatNumTotal = "";
16769  }
16770  if(Ptr2 == LocServiceTimesVector.end())
16771  {
16772  break;
16773  }
16774  if(Ptr2->Location != Ptr1->Location)
16775  {
16776  break;
16777  }
16778  }
16779  if(Ptr2 == LocServiceTimesVector.end())
16780  {
16781  break;
16782  }
16783  }
16784  }
16785  if(!ArrivalsPrinted)
16786  {
16787  TTFile3 << "Nothing to report for arrivals";
16788  }
16789  TTFile3 << "\n\n\n";
16790  }
16791  //end of routine for arrivals
16792 
16793  //departures
16794  if(DepChecked)
16795  {
16796  //sort in DepTime order for each location
16797  Ptr1 = LocServiceTimesVector.begin();
16798  Ptr2 = Ptr1 + 1;
16799  while(Ptr2 != LocServiceTimesVector.end())
16800  {
16801  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16802  {
16803  Ptr2++;
16804  if(Ptr2 == LocServiceTimesVector.end())
16805  {
16806  break;
16807  }
16808  }
16809  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
16810  Ptr1 = Ptr2; //first entry with next name
16811  if(Ptr2 != LocServiceTimesVector.end())
16812  {
16813  Ptr2++;
16814  }
16815  }
16816 
16817  //routine for departures - number of trains departing within the specified range with services listed at the end
16818  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
16819  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16820  MinuteString = " minutes";
16821  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16822  if(DepRange == 1)
16823  {
16824  MinuteString = " minute";
16825  }
16826  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
16827  TTFile3 << ",Platforms,Trains\n\n";
16828 
16829  Ptr1 = LocServiceTimesVector.begin();
16830  Ptr2 = Ptr1 + 1;
16831  while(Ptr2 != LocServiceTimesVector.end())
16832  {
16833  PreviousService = "";
16834  NumTrainsAtLoc = 0;
16835  ServiceAndRepeatNumTotal = "";
16836  NumPlats = 0;
16837  NumPlatsAtThisLocCalculated = false;
16838  BasicTime = "";
16839  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16840  {
16841  PreviousService = "";
16842  NumTrainsAtLoc = 0;
16843  ServiceAndRepeatNumTotal = "";
16844  NumPlats = 0;
16845  NumPlatsAtThisLocCalculated = false;
16846  BasicTime = "";
16847  Ptr1++;
16848  Ptr2++;
16849  if(Ptr2 == LocServiceTimesVector.end())
16850  {
16851  break;
16852  }
16853  }
16854  if(Ptr2 == LocServiceTimesVector.end())
16855  {
16856  break;
16857  }
16858  while(Ptr2->Location == Ptr1->Location)
16859  {
16860  PreviousService = "";
16861  NumTrainsAtLoc = 0;
16862  ServiceAndRepeatNumTotal = "";
16863  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16864  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16865  {
16866  break;
16867  }
16868  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
16869  {
16870  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
16871  Ptr1++;
16872  Ptr2++;
16873  if(Ptr2 == LocServiceTimesVector.end())
16874  {
16875  break;
16876  }
16877  if(Ptr2->Location != Ptr1->Location)
16878  {
16879  break;
16880  }
16881  }
16882  if(Ptr2 == LocServiceTimesVector.end())
16883  {
16884  break;
16885  }
16886  if(Ptr2->Location != Ptr1->Location)
16887  {
16888  break;
16889  }
16890  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
16891  {
16892  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
16893  {
16894  break;
16895  }
16896  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16897  {
16898  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
16899  NumPlatsAtThisLocCalculated = true;
16900  }
16901  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16902  {
16903  if(ServiceAndRepeatNumTotal == "")
16904  {
16905  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16906  NumTrainsAtLoc = 1;
16907  }
16908  else
16909  {
16910  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16911  }
16912  }
16913  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16914  if(ServiceAndRepeatNumTotal == "")
16915  {
16916  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16917  NumTrainsAtLoc = 1;
16918  }
16919  else
16920  {
16921  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16922  }
16923  Ptr1 = Ptr2;
16924  Ptr2++;
16925  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16926  {
16927  int MaxNumberOfSameDirections = 0;
16928  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16929  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16930  {
16931 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16932  TTFile3.close();
16933  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16934 // Utilities->CallLogPop(2225);
16935 // return false;
16936  }
16937  AnsiString Asterisk = "";
16938  if(MaxNumberOfSameDirections >= NumPlats)
16939  {
16940  Asterisk = "* ";
16941  }
16942  //print out a single line for number of trains at loc with all service refs
16943  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16944  DeparturesPrinted = true;
16945  ServiceAndRepeatNumTotal = "";
16946  }
16947  if(Ptr2 == LocServiceTimesVector.end())
16948  {
16949  break;
16950  }
16951  if(Ptr2->Location != Ptr1->Location)
16952  {
16953  break;
16954  }
16955  }
16956  if(Ptr2 == LocServiceTimesVector.end())
16957  {
16958  break;
16959  }
16960  }
16961  }
16962  if(!DeparturesPrinted)
16963  {
16964  TTFile3 << "Nothing to report for departures";
16965  }
16966  TTFile3 << "\n\n\n";
16967  }
16968  //end of routine for departures
16969 
16970 
16971  //list trains at locations at same time
16972 
16973  if(AtLocChecked)
16974  {
16975  //sort in AtLocTime order for each location
16976  Ptr1 = LocServiceTimesVector.begin();
16977  Ptr2 = Ptr1 + 1;
16978  while(Ptr2 != LocServiceTimesVector.end())
16979  {
16980  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16981  {
16982  Ptr2++;
16983  if(Ptr2 == LocServiceTimesVector.end())
16984  {
16985  break;
16986  }
16987  }
16988  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16989  Ptr1 = Ptr2; //first entry with next name
16990  if(Ptr2 != LocServiceTimesVector.end())
16991  {
16992  Ptr2++;
16993  }
16994  }
16995 
16996  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16997  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16998  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16999  TTFile3 << ",Platforms,Trains,\n\n";
17000  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17001  Ptr1 = LocServiceTimesVector.begin();
17002  Ptr2 = Ptr1 + 1;
17003  while(Ptr2 != LocServiceTimesVector.end())
17004  {
17005  PreviousService = "";
17006  ServiceAndRepeatNumTotal = "";
17007  NumTrainsAtLoc = 0;
17008  NumPlats = 0;
17009  NumPlatsAtThisLocCalculated = false;
17010  FrhCount = 0;
17011  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17012  {
17013  PreviousService = "";
17014  ServiceAndRepeatNumTotal = "";
17015  NumTrainsAtLoc = 0;
17016  NumPlats = 0;
17017  NumPlatsAtThisLocCalculated = false;
17018  FrhCount = 0;
17019  Ptr1++;
17020  Ptr2++;
17021  if(Ptr2 == LocServiceTimesVector.end())
17022  {
17023  break;
17024  }
17025  }
17026  if(Ptr2 == LocServiceTimesVector.end())
17027  {
17028  break;
17029  }
17030  while(Ptr2->Location == Ptr1->Location)
17031  {
17032  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17033  {
17034  FrhCount++;
17035  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17036  }
17037  PreviousService = "";
17038  NumTrainsAtLoc = 0;
17039  ServiceAndRepeatNumTotal = "";
17040  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17041  {
17042  break;
17043  }
17044  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17045  {
17046  Ptr1++;
17047  if(Ptr1->FrhMarker == "Frh")
17048  {
17049  FrhCount++;
17050  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17051  }
17052  Ptr2++;
17053  if(Ptr2 == LocServiceTimesVector.end())
17054  {
17055  break;
17056  }
17057  if(Ptr2->Location != Ptr1->Location)
17058  {
17059  break;
17060  }
17061  }
17062  if(Ptr2 == LocServiceTimesVector.end())
17063  {
17064  break;
17065  }
17066  if(Ptr2->Location != Ptr1->Location)
17067  {
17068  break;
17069  }
17070  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17071  {
17072  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17073  {
17074  break;
17075  }
17076  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17077  {
17078  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17079  NumPlatsAtThisLocCalculated = true;
17080  }
17081  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17082  {
17083  if(ServiceAndRepeatNumTotal == "")
17084  {
17085  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17086  NumTrainsAtLoc = 1;
17087  }
17088  else
17089  {
17090  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17091  }
17092  }
17093  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17094  if(ServiceAndRepeatNumTotal == "")
17095  {
17096  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17097  NumTrainsAtLoc = 1;
17098  }
17099  else
17100  {
17101  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17102  }
17103  Ptr1 = Ptr2;
17104  if(Ptr1->FrhMarker == "Frh")
17105  {
17106  FrhCount++;
17107  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17108  }
17109  Ptr2++;
17110  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17111  {
17112 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17113 //new text //don't print out if all remainers or if only 1 train at loc
17114  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17115 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17116 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17117  {
17118  AnsiString Asterisk = "";
17119  if(NumTrainsAtLoc > NumPlats)
17120  {
17121  Asterisk = "* ";
17122  }
17123  //print out a single line for number of trains at loc with all service refs
17124  if(FrhCount == 0)
17125  {
17126  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17127  }
17128  else if(FrhCount == 1)
17129  {
17130  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17131  }
17132  else
17133  {
17134  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17135  }
17136  LastFrhCount = FrhCount;
17137  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17138  AtLocsPrinted = true;
17139  ServiceAndRepeatNumTotal = "";
17140  }
17141  }
17142  if(Ptr2 == LocServiceTimesVector.end())
17143  {
17144  break;
17145  }
17146  if(Ptr2->Location != Ptr1->Location)
17147  {
17148  break;
17149  }
17150  }
17151  if(Ptr2 == LocServiceTimesVector.end())
17152  {
17153  break;
17154  }
17155  }
17156  }
17157  if(!AtLocsPrinted)
17158  {
17159  TTFile3 << "Nothing to report for trains at locations";
17160  }
17161  TTFile3 << "\n\n\n";
17162  //end of simultaneous AtLocs
17163 
17164 /*
17165  //print out the full vector here for testing purposes
17166  TTFile3 << "Full LocServiceTimesVector\n\n";
17167  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17168 
17169  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17170  {
17171  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17172  }
17173 
17174  TTFile3 << "\n\n\n";
17175 */
17176  }
17177  TTFile3.close();
17178  Utilities->CallLogPop(2212);
17179  return(true);
17180  }
17181 
17182  catch(const Exception &e) //non error catch
17183  {
17184  AnsiString TTErrorFileName = "Analysis Error.txt";
17185  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
17186  std::ofstream TTError(TTErrorFileName.c_str());
17187  if(TTError == 0)
17188  {
17189  ShowMessage("Analysis error file failed to open - can't be created");
17190  Utilities->CallLogPop(2233);
17191  return(false);
17192  }
17193  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17194  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
17195  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
17196  TTError.close();
17197  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
17198  Utilities->CallLogPop(2226);
17199  return(false);
17200  }
17201 }
17202 
17203 // ---------------------------------------------------------------------------
17204 
17205 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
17206 {
17207 //convert times to integer minutes
17208  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
17209  if((Time1 == "") || (Time2 == ""))
17210  {
17211  Utilities->CallLogPop(2213);
17212  return(false);
17213  }
17214  int Mins = Time1.SubString(4,2).ToInt();
17215  int Hours = Time1.SubString(1,2).ToInt();
17216  int Time1Mins = (Hours * 60) + Mins;
17217  Mins = Time2.SubString(4,2).ToInt();
17218  Hours = Time2.SubString(1,2).ToInt();
17219  int Time2Mins = (Hours * 60) + Mins;
17220  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
17221  {
17222  Utilities->CallLogPop(2214);
17223  return(true);
17224  }
17225  Utilities->CallLogPop(2215);
17226  return(false);
17227 }
17228 
17229 // ---------------------------------------------------------------------------
17230 
17231 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
17232  bool &AnalysisError, int &MaxNumberOfSameDirections)
17233 {
17234  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
17235 
17236  try
17237  {
17238  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
17239  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
17240  int SCPos = 0;
17241  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
17242  //first change every second comma in Input to a semicolon so can separate services but keep times with services
17243  bool EvenComma = false;
17244  for(int x = 1; x <= Input.Length(); x++)
17245  {
17246  TempStr1 = Input[x];
17247  if(TempStr1 == AnsiString(',') && EvenComma)
17248  {
17249  TempStr2 += ';';
17250  }
17251  else
17252  {
17253  TempStr2 += Input[x];
17254  }
17255  if(TempStr1 == AnsiString(','))
17256  {
17257  EvenComma = !EvenComma;
17258  }
17259  }
17260  //load up the list of services with associated times
17261  while(TempStr2.Length() > 0)
17262  {
17263  SCPos = TempStr2.Pos(';');
17264  if(SCPos > 0) //0 if not found, as won't be when only one service left
17265  {
17266  OneService = TempStr2.SubString(1, SCPos - 1);
17267  ServiceList.push_back(OneService);
17268  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
17269  }
17270  else //no semicolon so looking at last (or only) element
17271  {
17272  ServiceList.push_back(TempStr2);
17273  TempStr2 = "";
17274  }
17275  }
17276  ServiceList.sort(); // alphabetical order
17277  ServiceList.unique(); //remove duplicates
17278  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17279 
17280  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
17281  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
17282  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
17283  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
17284 
17285  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17286  {
17287  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
17288  }
17289  SLIt3 = ServiceList.end();
17290  SLIt3--; //so points to last element
17291  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
17292  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
17293  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
17294  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
17295  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
17296  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
17297 
17298  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
17299  {
17300  SLIt = SLIt1;
17301  SLIt++; //so points to one after SLIt1
17302  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
17303  {
17304  continue; //already allocated so skip to the next
17305  }
17306  else
17307  {
17308  CommaPos1 = SLIt1->Pos(','); //can't be 0
17309  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
17310  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
17311  SpacePos = ServiceRef1.Pos(' ');
17312  RepeatNum1 = 0;
17313  if(SpacePos > 0) //otherwise it's already correct
17314  {
17315  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17316  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
17317  if(RepeatInfo1[1] == 'F')
17318  {
17319  RepeatNum1 = 0;
17320  }
17321  else
17322  {
17323  SpacePos = RepeatInfo1.Pos(' ');
17324  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
17325  }
17326  }
17327  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
17328  //but this includes the "&0" etc so need to strip these
17329  AmpersandPos = AnsiTime1.Pos('&');
17330  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
17331 
17332  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
17333  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
17334  {
17335  throw Exception("ASCLIt1 Error in " + Input);
17336  }
17337  ServiceCallingLocsList1 = ASCLIt1->second;
17338  AmpersandPos = SLIt1->Pos('&');
17339  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17340  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
17341 
17342  SameDirectionCount = 1;
17343  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
17344  {
17345  CommaPos2 = SLIt2->Pos(','); //can't be 0
17346  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
17347  //but this contains "(First service..." etc so need to strip these
17348  SpacePos = ServiceRef2.Pos(' ');
17349  RepeatNum2 = 0;
17350  if(SpacePos > 0) //otherwise it's already correct
17351  {
17352  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17353  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
17354  if(RepeatInfo2[1] == 'F')
17355  {
17356  RepeatNum2 = 0;
17357  }
17358  else
17359  {
17360  SpacePos = RepeatInfo2.Pos(' ');
17361  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
17362  }
17363  }
17364  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
17365  //but this includes the "&0" etc so need to strip these
17366  AmpersandPos = AnsiTime2.Pos('&');
17367  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
17368 
17369  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
17370  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
17371  {
17372  throw Exception("ASCLIt2 Error in " + Input);
17373  }
17374  ServiceCallingLocsList2 = ASCLIt2->second;
17375  //now compare the two
17376  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
17377  {
17378  int AmpersandPos = SLIt2->Pos('&');
17379  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17380  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
17381  SameDirectionCount++;
17382  }
17383  }
17384  if(SameDirectionCount > MaxNumberOfSameDirections)
17385  {
17386  MaxNumberOfSameDirections = SameDirectionCount;
17387  }
17388  }
17389  }
17390 
17391  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
17392  {
17393  //any existing direction so allocate it now
17394  AmpersandPos = SLIt3->Pos('&');
17395  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17396  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
17397  }
17398  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
17399  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17400  {
17401  //extract the DirectionMarker as an integer
17402  AmpersandPos = SLIt->Pos('&');
17403  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
17404  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
17405  DirectionMarker = DirectionMarkerString.ToInt();
17406  AnsiString DirectionSuffix = "";
17407  char c;
17408  if(DirectionMarker < 27)
17409  {
17410  c = 64 + DirectionMarker; //so 1 -> 'A'
17411  DirectionSuffix = "," + AnsiString(c);
17412  }
17413  else if(DirectionMarker < 53)
17414  {
17415  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
17416  DirectionSuffix = ",A" + AnsiString(c);
17417  }
17418  else
17419  {
17420  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
17421  }
17422  *SLIt = ServiceWithoutMarker + DirectionSuffix;
17423  }
17424  //now prepare the final consolidated output
17425  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17426  {
17427  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17428  }
17429  if(Output.Length() > 0)
17430  {
17431  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17432  }
17433  Utilities->CallLogPop(2216);
17434  return(Output);
17435  }
17436 
17437  catch(const Exception &e) //non error catch
17438  {
17439  AnalysisError = true;
17440  Utilities->CallLogPop(2227);
17441  return(e.Message);
17442  }
17443 }
17444 
17445 // ---------------------------------------------------------------------------
17446 
17447 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
17448 {
17449  //similar to above but doesn't include times in the input
17450  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
17451  AnsiString InternalInput = Input, Output = "", OneService = "";
17452  int CommaPos = 0;
17453  std::list<AnsiString> ServiceList;
17454  //load up the list
17455  while(InternalInput.Length() > 0)
17456  {
17457  CommaPos = InternalInput.Pos(',');
17458  if(CommaPos > 0) //0 if not found, as won't be when only one service left
17459  {
17460  OneService = InternalInput.SubString(1, CommaPos - 1);
17461  ServiceList.push_back(OneService);
17462  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
17463  }
17464  else //no comma so looking at last (or only) element
17465  {
17466  ServiceList.push_back(InternalInput);
17467  InternalInput = "";
17468  }
17469  }
17470 
17471  ServiceList.sort(); // alphabetical order
17472  ServiceList.unique(); //remove duplicates
17473  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17474  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17475  {
17476  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17477  }
17478  if(Output.Length() > 0)
17479  {
17480  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17481  }
17482  Utilities->CallLogPop(2217);
17483  return(Output);
17484 }
17485 
17486 // ---------------------------------------------------------------------------
17487 
17488 
17489 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
17490  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
17491 {
17492  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
17493  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
17494 
17495  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
17496  //general list pointers, LocPtrs point to Location in the two lists
17497 
17498  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
17499  //for List1
17500  bool LocFound = false;
17501  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
17502  int IncMinutes;
17503  TDateTime FirstServiceTime;
17504 
17505  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
17506  int Ref1Target = 0, Ref1Count = 0;
17507  int SlashPos = Ref1.Pos('/');
17508  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
17509  {
17510  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
17511  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17512  }
17513  int Ref2Target = 0, Ref2Count = 0;
17514  SlashPos = Ref2.Pos('/');
17515  if(SlashPos > 0) //if 0 leave as is
17516  {
17517  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
17518  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17519  }
17520  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
17521  {
17522  //even if others have same names. But if there are cdt's then need to refind the correct service
17523  if((*ListPtr1) == Location) //
17524  {
17525  LocPtr1 = ListPtr1; //may be modified later
17526  LocFound = true;
17527  }
17528  if(ListPtr1->SubString(1, 3) == "%%%")
17529  {
17530  AnsiString CDTTime = ListPtr1->SubString(4, 5);
17531  //now adjust the time to correspond to the repeat if there is one
17532  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
17533  {
17534  IncMinutes = -1;
17535  FirstServiceTime = TDateTime(-1);
17536  bool BreakFlag = false;
17537  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17538  {
17539  if(TDVIt->ServiceReference == Ref1)
17540  {
17541  if(Ref1Target > Ref1Count)
17542  {
17543  Ref1Count++;
17544  continue;
17545  }
17546  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17547  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17548  {
17549  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17550  {
17551  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
17552  BreakFlag = true;
17553  break;
17554  }
17555  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
17556  {
17557  FirstServiceTime = AVIt->ArrivalTime;
17558  BreakFlag = true;
17559  break;
17560  }
17561  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17562  {
17563  FirstServiceTime = AVIt->DepartureTime;
17564  BreakFlag = true;
17565  break;
17566  }
17567  }
17568  if(BreakFlag)
17569  {
17570  break;
17571  }
17572  }
17573  }
17574  if(IncMinutes == -1)
17575  {
17576  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17577  }
17578  if(FirstServiceTime == TDateTime(-1))
17579  {
17580  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17581  }
17582  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
17583  }
17584  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
17585  {
17586  LocFound = false;
17587  continue;
17588  }
17589  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
17590  {
17591  break;
17592  }
17593  if(Time1 > CDTTime) //not there yet so go on
17594  {
17595  LocFound = false;
17596  continue;
17597  }
17598  if(Time1 < CDTTime) //gone too far so can stop now
17599  {
17600  break;
17601  }
17602  }
17603  }
17604  if(!LocFound) //have to find it in both lists
17605  {
17606  Utilities->CallLogPop(2228);
17607  return( false);
17608  }
17609  //for List2
17610  LocFound = false;
17611  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
17612  {
17613  if((*ListPtr2) == Location)
17614  {
17615  LocPtr2 = ListPtr2; //may be modified later
17616  LocFound = true;
17617  }
17618  if(ListPtr2->SubString(1, 3) == "%%%")
17619  {
17620  AnsiString CDTTime = ListPtr2->SubString(4, 5);
17621  //now adjust the time to correspond to the repeat if there is one
17622  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
17623  {
17624  IncMinutes = -1;
17625  FirstServiceTime = TDateTime(-1);
17626  bool BreakFlag = false;
17627  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17628  {
17629  if(TDVIt->ServiceReference == Ref2)
17630  {
17631  if(Ref2Target > Ref2Count)
17632  {
17633  Ref2Count++;
17634  continue;
17635  }
17636  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17637  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17638  {
17639  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17640  {
17641  FirstServiceTime = AVIt->EventTime;
17642  BreakFlag = true;
17643  break;
17644  }
17645  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
17646  {
17647  FirstServiceTime = AVIt->ArrivalTime;
17648  BreakFlag = true;
17649  break;
17650  }
17651  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17652  {
17653  FirstServiceTime = AVIt->DepartureTime;
17654  BreakFlag = true;
17655  break;
17656  }
17657  }
17658  if(BreakFlag)
17659  {
17660  break;
17661  }
17662  }
17663  }
17664  if(IncMinutes == -1)
17665  {
17666  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17667  }
17668  if(FirstServiceTime == TDateTime(-1))
17669  {
17670  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17671  }
17672  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
17673  }
17674  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
17675  {
17676  LocFound = false;
17677  continue;
17678  }
17679  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
17680  {
17681  break;
17682  }
17683  if(Time2 > CDTTime) //not there yet so go on
17684  {
17685  LocFound = false;
17686  continue;
17687  }
17688  if(Time2 < CDTTime) //gone too far so can stop now
17689  {
17690  break;
17691  }
17692  }
17693  }
17694  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
17695  {
17696  Utilities->CallLogPop(2229);
17697  return( false);
17698  }
17699  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
17700  //set ListPtr1 to the search start position
17701  if(Arrival)
17702  {
17703  LP1 = List1.begin();
17704  LP1--; //now points to before the first entry
17705  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
17706  {
17707  if(ListPtr1 == List1.begin())
17708  {
17709  break;
17710  }
17711  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
17712  {
17713  ListPtr1++; //point to one past the cdt
17714  break;
17715  }
17716  }
17717  //set ListPtr2 to the search start position
17718  LP2 = List2.begin();
17719  LP2--; //now points to before the first entry
17720  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
17721  {
17722  if(ListPtr2 == List2.begin())
17723  {
17724  break;
17725  }
17726  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
17727  {
17728  ListPtr2++; //point to one past the cdt
17729  break;
17730  }
17731  }
17732  //ListPtr1 & 2 now at search start position
17733  LP1 = ListPtr1;
17734  LP2 = ListPtr2;
17735  //now search forwards, i.e. for common locations before Location
17736  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17737  {
17738  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
17739  {
17740  break;
17741  }
17742  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
17743  {
17744  break;
17745  }
17746  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17747  {
17748  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
17749  {
17750  break;
17751  }
17752  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
17753  {
17754  break;
17755  }
17756  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
17757  {
17758  Utilities->CallLogPop(2230);
17759  return( true);
17760  }
17761  }
17762  }
17763  }
17764 
17765  //now, for the departure analysis, reset the start positions and search locations after Location
17766 
17767  else
17768  {
17769  LP1 = LocPtr1;
17770  LP1++; //start at one past the location itself
17771  LP2 = LocPtr2;
17772  LP2++;
17773  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17774  {
17775  if(ListPtr1 == List1.end()) //reached end point so stop
17776  {
17777  break;
17778  }
17779  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
17780  {
17781  break;
17782  }
17783  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17784  {
17785  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
17786  {
17787  break;
17788  }
17789  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
17790  {
17791  break;
17792  }
17793  if((*ListPtr1) == (*ListPtr2)) //found a common later location
17794  {
17795  Utilities->CallLogPop(2231);
17796  return( true);
17797  }
17798  }
17799  }
17800  }
17801  Utilities->CallLogPop(2232);
17802  return( false);
17803 }
17804 
17805 // ---------------------------------------------------------------------------
17806 
17807 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
17808 {
17809  // changed at v2.7.0 to show allowable exit elements
17810  if(ExitList.empty())
17811  {
17812  return("");
17813  }
17814  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
17815  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
17816  AnsiString ExitLocList = "";
17817  AllowedExits = "";
17818 
17819  unsigned int Counter = 0;
17820  for(TExitListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
17821  {
17822  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
17823  Counter++;
17824  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
17825  {
17826  ExitLocList += "\n";
17827  }
17828  }
17829  if(StartName == "")
17830  {
17831  if(ExitList.size() == 1)
17832  {
17833  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
17834  Utilities->CallLogPop(1571);
17835  return(" at " + ID);
17836  }
17837  else
17838  {
17839  Utilities->CallLogPop(1572);
17840  if(ExitList.size() < 4)
17841  {
17842  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17843  return("");
17844  }
17845  else
17846  {
17847  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17848  return("");
17849  }
17850  }
17851  }
17852  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
17853  {
17854  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
17855  {
17856  Utilities->CallLogPop(1570);
17857  if(ExitList.size() < 4)
17858  {
17859  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17860  return("");
17861  }
17862  else
17863  {
17864  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17865  return("");
17866  }
17867  }
17868  }
17869  Utilities->CallLogPop(1569);
17870  if(ExitList.size() < 4)
17871  {
17872  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17873  return(" at " + StartName);
17874  }
17875  else
17876  {
17877  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17878  return(" at " + StartName);
17879  }
17880 }
17881 
17882 // ---------------------------------------------------------------------------
17883 /* can't trust this as locations within a vector may not be contiguous
17884  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
17885  {
17886  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
17887  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
17888  //must be preceded by a TimeLoc departure
17889  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
17890  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
17891  {
17892  if((AVPtr + x) < TDEPtr->ActionVector.end())
17893  {
17894  AnsiString xx = (AVPtr + x)->Command;//test
17895  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
17896  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
17897  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
17898  {
17899  Utilities->CallLogPop();
17900  return false;
17901  }
17902  else if((AVPtr + x)->SequenceType == Finish)
17903  {
17904  Utilities->CallLogPop();
17905  return true;
17906  }
17907  }
17908  }
17909  Utilities->CallLogPop();
17910  return false;
17911  }
17912 */
17913 // ---------------------------------------------------------------------------
17914 
17915 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
17916 {
17917  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
17918  AnsiString FormatStr = "####0.0";
17919  AnsiString AvLateArrMins = "";
17920  AnsiString AvEarlyArrMins = "";
17921  AnsiString AvLatePassMins = "";
17922  AnsiString AvEarlyPassMins = "";
17923  AnsiString AvLateDepMins = "";
17924  AnsiString AvLateExitMins = "";
17925  AnsiString AvEarlyExitMins = "";
17926 
17927  if(LateArrivals > 0)
17928  {
17929  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
17930  }
17931  if(EarlyArrivals > 0)
17932  {
17933  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
17934  }
17935  if(LatePasses > 0)
17936  {
17937  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
17938  }
17939  if(EarlyPasses > 0)
17940  {
17941  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
17942  }
17943  if(LateDeps > 0)
17944  {
17945  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
17946  }
17947  if(LateExits > 0) //added at v2.9.1
17948  {
17949  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
17950  }
17951  if(EarlyExits > 0) //added at v2.9.1
17952  {
17953  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
17954  }
17955  PerfFile << '\n' << '\n' << "***************************************";
17956  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
17957 
17958  if(OnTimeArrivals != 1)
17959  {
17960  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
17961  }
17962  else
17963  {
17964  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
17965  }
17966  if(LateArrivals > 1)
17967  {
17968  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
17969  }
17970  else if(LateArrivals == 1)
17971  {
17972  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17973  }
17974  else
17975  {
17976  PerfFile << LateArrivals << " late arrivals" << '\n';
17977  }
17978  if(EarlyArrivals > 1)
17979  {
17980  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17981  }
17982  else if(EarlyArrivals == 1)
17983  {
17984  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17985  }
17986  else
17987  {
17988  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17989  }
17990  if(OnTimePasses != 1)
17991  {
17992  PerfFile << OnTimePasses << " on-time passes" << '\n';
17993  }
17994  else
17995  {
17996  PerfFile << OnTimePasses << " on-time pass" << '\n';
17997  }
17998  if(LatePasses > 1)
17999  {
18000  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
18001  }
18002  else if(LatePasses == 1)
18003  {
18004  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
18005  }
18006  else
18007  {
18008  PerfFile << LatePasses << " late passes" << '\n';
18009  }
18010  if(EarlyPasses > 1)
18011  {
18012  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
18013  }
18014  else if(EarlyPasses == 1)
18015  {
18016  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
18017  }
18018  else
18019  {
18020  PerfFile << EarlyPasses << " early passes" << '\n';
18021  }
18022 
18023  if(OnTimeExits != 1) //this batch added at v2.9.1
18024  {
18025  PerfFile << OnTimeExits << " on-time exits" << '\n';
18026  }
18027  else
18028  {
18029  PerfFile << OnTimeExits << " on-time exit" << '\n';
18030  }
18031  if(LateExits > 1)
18032  {
18033  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
18034  }
18035  else if(LateExits == 1)
18036  {
18037  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
18038  }
18039  else
18040  {
18041  PerfFile << LateExits << " late exits" << '\n';
18042  }
18043  if(EarlyExits > 1)
18044  {
18045  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
18046  }
18047  else if(EarlyExits == 1)
18048  {
18049  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
18050  }
18051  else
18052  {
18053  PerfFile << EarlyExits << " early exits" << '\n';
18054  }
18055 
18056  if(OnTimeDeps != 1)
18057  {
18058  PerfFile << OnTimeDeps << " on-time departures" << '\n';
18059  }
18060  else
18061  {
18062  PerfFile << OnTimeDeps << " on-time departure" << '\n';
18063  }
18064  if(LateDeps > 1)
18065  {
18066  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
18067  }
18068  else if(LateDeps == 1)
18069  {
18070  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
18071  }
18072  else
18073  {
18074  PerfFile << LateDeps << " late departures" << '\n';
18075  }
18076  TDateTime TempExcessLCDownTime;
18077  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
18078  {
18079 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
18080  //later perf summaries with lower values, changed at v2.8.0
18081 // {
18082  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
18083 // }
18084 /*
18085  else
18086  {
18087  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
18088  }
18089 */
18090  if(TempExcessLCDownTime > TDateTime(0))
18091  {
18092  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
18093  }
18094  }
18095 
18096  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
18097 
18098  if(ExcessLCDownMins > 0.1)
18099  {
18100  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
18101  }
18102  if(MissedStops != 1)
18103  {
18104  PerfFile << MissedStops << " missed stops" << '\n';
18105  }
18106  else
18107  {
18108  PerfFile << MissedStops << " missed stop" << '\n';
18109  }
18110  if(OtherMissedEvents != 1)
18111  {
18112  PerfFile << OtherMissedEvents << " other missed events" << '\n';
18113  }
18114  else
18115  {
18116  PerfFile << OtherMissedEvents << " other missed event" << '\n';
18117  }
18118  if(UnexpectedExits != 1)
18119  {
18120  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
18121  }
18122  else
18123  {
18124  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
18125  }
18126  if(IncorrectExits != 1)
18127  {
18128  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
18129  }
18130  else
18131  {
18132  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
18133  }
18134  if(NumFailures != 1)
18135  {
18136  PerfFile << NumFailures << " train failures" << '\n';
18137  }
18138  else
18139  {
18140  PerfFile << NumFailures << " train failure" << '\n';
18141  }
18142  if(AvHoursIntValue > 0)
18143  {
18144  if(AvHoursIntValue == 1)
18145  {
18146  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
18147  }
18148  else
18149  {
18150  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
18151  }
18152  }
18153  AnsiString AvLateMinsLocsNotReached = "";
18154 
18156  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
18157  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
18158 
18159  if(LocsNotReached > 0)
18160  {
18161  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
18162  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
18163  }
18164  if(SPADRisks != 1)
18165  {
18166  PerfFile << SPADRisks << " SPAD risks" << '\n';
18167  }
18168  else
18169  {
18170  PerfFile << SPADRisks << " SPAD risk" << '\n';
18171  }
18172  if(SPADEvents != 1)
18173  {
18174  PerfFile << SPADEvents << " SPADs" << '\n';
18175  }
18176  else
18177  {
18178  PerfFile << SPADEvents << " SPAD" << '\n';
18179  }
18180  if(Derailments != 1)
18181  {
18182  PerfFile << Derailments << " derailments" << '\n';
18183  }
18184  else
18185  {
18186  PerfFile << Derailments << " derailment" << '\n';
18187  }
18188  if(CrashedTrains != 1)
18189  {
18190  PerfFile << CrashedTrains << " crashed trains" << '\n';
18191  }
18192  else
18193  {
18194  PerfFile << CrashedTrains << " crashed train" << '\n';
18195  }
18196  PerfFile << '\n' << "***************************************" << '\n';
18197 
18198  bool DerailSPADFlag = false, CrashFlag = false;
18199 
18200  int OverallScorePercent = 100;
18201  int TotArrDepExit = 0;
18202  double TotLateMinsFactor = 1;
18203  double MissedStopAndSPADRiskFactor = 1;
18204  double NetNegFactor = 1;
18205 
18207  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1
18208  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
18209  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
18210  // 'no timetabled departures... message, which was inappropriate
18211 
18212  if((SPADEvents > 0) || (Derailments > 0))
18213  {
18214  OverallScorePercent = 5; // overrides other calculations
18215  DerailSPADFlag = true;
18216  }
18217  if(CrashedTrains > 0)
18218  {
18219  OverallScorePercent = 0; // overrides other calculations
18220  CrashFlag = true;
18221  }
18222  if(OverallScorePercent == 100)
18223  {
18224  if(TotArrDepExit > 0)
18225  {
18226  TotLateMinsFactor =
18228  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
18229  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
18230  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
18231  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
18232  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
18233  // of arrivals & departures, where 4% = half, 8% = a quarter etc
18234  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
18235  // NetNegfactor: product of the above two
18236  OverallScorePercent = 100 * NetNegFactor;
18237  }
18238  }
18239  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
18240  // flag condits added at v1.1.4 - see above for what the error was
18241  {
18242  AnsiString OneFailureString = ", though the failure would account for some poor performance";
18243  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
18244  AnsiString AddedString = "";
18245  if(NumFailures == 1)
18246  {
18247  AddedString = OneFailureString;
18248  }
18249  if(NumFailures > 1)
18250  {
18251  AddedString = TwoOrMoreFailureString;
18252  }
18253  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
18254  AnsiString Rating = "";
18255  if(OverallScorePercent == 100)
18256  {
18257  Rating = "Perfect!";
18258  }
18259  else if(OverallScorePercent >= 95)
18260  {
18261  Rating = "Excellent";
18262  }
18263  else if(OverallScorePercent >= 90)
18264  {
18265  Rating = "Very good";
18266  }
18267  else if(OverallScorePercent >= 80)
18268  {
18269  Rating = "Good";
18270  }
18271  else if(OverallScorePercent >= 70)
18272  {
18273  Rating = "Fair";
18274  }
18275  else if(OverallScorePercent >= 60)
18276  {
18277  Rating = "Unacceptable" + AddedString;
18278  }
18279  else if(OverallScorePercent >= 50)
18280  {
18281  Rating = "Poor" + AddedString;
18282  }
18283  else if(OverallScorePercent >= 40)
18284  {
18285  Rating = "Bad" + AddedString;
18286  }
18287  else if(OverallScorePercent >= 30)
18288  {
18289  Rating = "Very bad" + AddedString;
18290  }
18291  else if(OverallScorePercent >= 20)
18292  {
18293  Rating = "Terrible" + AddedString;
18294  }
18295  else if(OverallScorePercent >= 10)
18296  {
18297  Rating = "Appalling" + AddedString;
18298  }
18299  else if(OverallScorePercent >= 5)
18300  {
18301  if(DerailSPADFlag)
18302  {
18303  Rating = "Disastrous - potential loss of life";
18304  }
18305  // SPADs/Derailments
18306  else
18307  {
18308  Rating = "Dire" + AddedString;
18309  }
18310  }
18311  else if(OverallScorePercent < 5)
18312  {
18313  if(CrashFlag)
18314  {
18315  Rating = "Catastrophic - loss of life"; // Crashes
18316  }
18317  else
18318  {
18319  Rating = "Abysmal";
18320  }
18321  }
18322  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
18323  }
18324  else
18325  {
18326  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
18327  }
18328  PerfFile << '\n' << "***************************************";
18329  Utilities->CallLogPop(1736);
18330 }
18331 
18332 // ---------------------------------------------------------------------------
18333 
18335 {
18336  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
18337  for(unsigned int x = 0; x < TrainVector.size(); x++)
18338  {
18339  TTrain &Train = TrainVectorAt(58, x);
18340  if(Train.Crashed)
18341  // can't use background colours for crashed & derailed because same colour
18342  {
18343  CrashWarning = true;
18344  }
18345  else if(Train.Derailed)
18346  // can't use background colours for crashed & derailed because same colour
18347  {
18348  DerailWarning = true;
18349  }
18350  else if(Train.BackgroundColour == clSPADBackground)
18351  // use colour as that changes as soon as passes signal
18352  {
18353  SPADWarning = true;
18354  }
18355  else if(Train.BackgroundColour == clTrainFailedBackground)
18356  {
18357  TrainFailedWarning = true;
18358  }
18359  else if(Train.BackgroundColour == clCallOnBackground)
18360  // use colour as also stopped at signal
18361  {
18362  CallOnWarning = true;
18363  }
18364  else if(Train.BackgroundColour == clSignalStopBackground)
18365  // use colour to distinguish from call-on
18366  {
18367  SignalStopWarning = true;
18368  }
18369  else if(Train.BackgroundColour == clBufferAttentionNeeded)
18370  // use colour to distinguish from ordinary buffer stop
18371  {
18372  BufferAttentionWarning = true;
18373  }
18374  }
18375  Utilities->CallLogPop(1796);
18376 }
18377 
18378 // ---------------------------------------------------------------------------
18379 
18381 {
18382  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
18383 
18384  // calculate lateness for running trains
18387  for(unsigned int x = 0; x < TrainVector.size(); x++)
18388  {
18389  TTrain &Train = TrainVectorAt(64, x);
18390  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
18391  AVEntryPtr++)
18392  {
18393  if(AVEntryPtr < Train.ActionVectorEntryPtr)
18394  {
18395  continue;
18396  }
18397  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18398  TTClockTime))
18399  {
18400  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
18402  }
18403 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
18404  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18405  TTClockTime))
18406  {
18407  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
18408  OperatingTrainArrDep++;
18409  }
18410 */
18411  }
18412  }
18413 
18414  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
18417 
18418  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
18419  {
18420  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
18421  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
18422  int IncrementalMinutes = 0;
18423  if(AVEntryLast.FormatType == Repeat)
18424  {
18425  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
18426  }
18427  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
18428  {
18429  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
18430  if(TTOD.RunningEntry != NotStarted)
18431  {
18432  continue;
18433  }
18434  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
18435  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
18436  bool TrainOperatingFlag = false;
18437  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
18438  {
18439  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
18440  {
18441  TrainOperatingFlag = true;
18442  break;
18443  }
18444  }
18445  if(TrainOperatingFlag)
18446  {
18447  continue;
18448  }
18449  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
18450  {
18451  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
18452  }
18453  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
18454  {
18455  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18456  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
18457  {
18458  break; // all the rest will also be greater (& default of -1 will be less)
18459  }
18460  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
18461  {
18462  break; // all the rest will also be greater (& default of -1 will be less)
18463  }
18464  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
18465  {
18466  break; // all the rest will also be greater (& default of -1 will be less)
18467  }
18468  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
18469  {
18470  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
18472  }
18473 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
18474  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
18475  {
18476  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
18477  NotStartedTrainArrDep++;
18478  }
18479 */
18480  }
18481  }
18482  }
18483  Utilities->CallLogPop(1894);
18484 }
18485 
18486 // ---------------------------------------------------------------------------
18487 
18489 // new v2.2.0 for OperatorActionPanel
18490 // clears entries then adds values for running trains then for continuation entries
18491 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
18492 {
18493  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
18494  OpTimeToActMultiMap.clear();
18495  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
18496 
18497  if(!TrainVector.empty())
18498  // build OpTimeToActMultiMap entries for running trains
18499  {
18500  AnsiString HeadCode;
18501  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
18502  int TrainID;
18503  THCandTrainPosParam HCandTrainPosParam;
18504  for(unsigned int x = 0; x < TrainVector.size(); x++)
18505  {
18506  HeadCode = TrainVectorAt(62, x).HeadCode;
18507  TrainID = TrainVectorAt(63, x).TrainID;
18508  HCandTrainPosParam.first = HeadCode;
18509  HCandTrainPosParam.second = TrainID;
18510  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
18511  if((TimeToAct >= 0) && (TimeToAct < 59.9))
18512  // -1 indicates don't display
18513  {
18514  OpTimeToActMultiMapEntry.first = TimeToAct;
18515  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18516  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18517  }
18518  }
18519  }
18520 /*
18521  * class TContinuationTrainExpectationEntry
18522  {
18523  public:
18524  AnsiString Description; ///< service description
18525  AnsiString HeadCode; ///< service headcode
18526  int RepeatNumber; ///< service RepeatNumber
18527  int IncrementalMinutes; ///< Repeat separation in minutes
18528  int IncrementalDigits; ///< Repeat headcode separation
18529  int VectorPosition; ///< TrackVectorPosition for the continuation element
18530  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
18531  };
18532 
18533  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
18534  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
18535  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
18536  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
18537 */
18538 
18540  // build OpTimeToActMultiMap entries for expected trains
18541  {
18542  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
18543  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
18544  float TimeToAct = 0; // minutes
18545  int DistanceToRedSignal = 0; // metres
18546  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
18547  // used to ensure only one train displayed for a given continuation
18548  ContinuationEntryVecPosVector.clear();
18549  bool LaterTrain = false;
18552  {
18553  LaterTrain = false;
18554  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
18555  {
18556  CTEIt++;
18557  continue; // not interested in running or exited trains
18558  }
18559  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
18560  {
18561  CTEIt++;
18562  continue;
18563  // don't include trains not entered yet when a train is already on the continuation
18564  }
18565  if(!ContinuationEntryVecPosVector.empty())
18566  {
18567  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
18568  {
18569  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
18570  {
18571  LaterTrain = true;
18572  ;
18573  // skip past remaining trains waiting to enter at same point
18574  break;
18575  }
18576  }
18577  }
18578  if(LaterTrain)
18579  {
18580  CTEIt++;
18581  continue;
18582  }
18583  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
18584  AnsiString HeadCode = CTEIt->second.HeadCode;
18585  float CurrentStopTime; // set to 0 at start of function
18586  float LaterStopTime; // set to 0 at start of function
18587  float RecoverableTime; // set to 0 at start of function
18588  int AvTrackSpeed; // set to 0 at start of function
18589  int TrainID = -1; // not yet allocated for train still to enter
18590  bool SigControlAndCanPassRedSignal = false;
18591  // doesn't apply for a continuation
18592  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
18593  // EntryPos always 0 for entering at a continuation
18594  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
18595  // at(1) to skip past the Start train value
18596  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
18597  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
18598  // for a train it's the one in front of LeadElement
18599  if(AvTrackSpeed < 30)
18600  {
18601  AvTrackSpeed = 30;
18602  }
18603  if(DistanceToRedSignal == -1)
18604  {
18605  TimeToAct = 60.0;
18606  }
18607  else
18608  {
18609  int Speed = AvTrackSpeed;
18610  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
18611  if(AvTrackSpeed > MaxSpeed)
18612  {
18613  Speed = MaxSpeed;
18614  }
18615  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
18616  // defined in timetable as under signaller control
18617  {
18618  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
18619  LaterStopTime = 0;
18620  }
18621  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
18622  // accel & decel taken into account in
18623  // CalcDistanceToRedSignalandStopTime
18624  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
18625  // don't need CurrentStopTime or RecoverableTime for continuation entries
18626  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
18627  TimeToAct += MinsBefEnter;
18628  }
18629  THCandTrainPosParam HCandTrainPosParam;
18630  HCandTrainPosParam.first = HeadCode;
18631  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
18632  // -1-CTE... because 2nd value covers TrainID if +ve &
18633  // continuation track vector position if -ve, -1 allows for vecpos being 0
18634  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
18635  {
18636  OpTimeToActMultiMapEntry.first = TimeToAct;
18637  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18638  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18639  }
18640  CTEIt++;
18641  }
18642  }
18643  Utilities->CallLogPop(2081);
18644 }
18645 
18646 // ---------------------------------------------------------------------------
18647 
18648 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
18649  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
18650  float &RecoverableTime, int &AvTrackSpeed)
18651 // new v2.2.0
18652 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
18653 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
18654 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
18655 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
18656 // aren't used - this means there is no display for the train in question
18657 {
18658  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
18659  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
18660  int DistanceToRedSignal = 0;
18661  int CumTrackSpeed = 0;
18662  // average track speed, in case need to use in time calc
18663  int TrackSpeedCount = 0;
18664 
18665  //below added at v2.6.1
18666  if(TrainID > -1)
18667  {
18668  TTrain &Train = TrainVectorAtIdent(51, TrainID);
18669  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
18670  Train.StationStopCalculated = false;
18671  }
18672  AvTrackSpeed = 0;
18673  int CurrentElement = TrackVectorPosition;
18674  int CurrentEntryPos = TrackVectorPositionEntryPos;
18675  int NextElement;
18676  int NextEntryPos;
18677  int NextExitPos;
18678 
18679  CurrentStopTime = 0;
18680  LaterStopTime = 0;
18681  RecoverableTime = 0;
18682  if(CurrentElement == -1) // end element, no action needed
18683  {
18684  Utilities->CallLogPop(2094);
18685  return(-1);
18686  }
18687  int CurrentExitPos;
18688 
18689  // get ExitPos for first element to be measured
18690  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
18691  {
18692  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
18693  {
18694  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
18695  {
18696  CurrentExitPos = 1;
18697  }
18698  else
18699  {
18700  CurrentExitPos = 3;
18701  }
18702  }
18703  else
18704  {
18705  CurrentExitPos = 0; // trailing point
18706  }
18707  }
18708  else
18709  {
18710  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
18711  }
18712  // get CumTrackSpeed for first measured element
18713 
18714  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
18715  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
18716 
18717  // check if currently stopped at a location, and if so add the remaining dwell time
18718  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
18719  if(TrainID > -1)
18720  // -1 for a continuation and can't be at a location as not yet entered
18721  {
18722  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
18724  // this used to deduct from RecoverableTime when arrive at a location
18725  if(Train.StoppedAtLocation)
18726  {
18727  if(Train.StoppedForTrainInFront)
18728  {
18729  Utilities->CallLogPop(2082);
18730  return(-1); // no action needed
18731  }
18732  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
18733  {
18734  Utilities->CallLogPop(2083);
18735  return(-1); // not due a departure so no action needed
18736  }
18737  else // due a departure
18738  {
18739  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
18740  // can't convert a TDateTime to a float directly
18741  CurrentStopTime = float(TimeToDepart);
18742  AVPtr++;
18743  }
18744  }
18745  }
18746  // check if CurrentElement is a red signal, but ok if autosig route after
18747  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18748  // ok if autosig route after red signal
18749  {
18750  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
18751  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
18752  int RouteNumber; // holder for referenced value, not used
18753  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18754  {
18755  Utilities->CallLogPop(2078);
18756  return(-1);
18757  }
18758  else if(SigControlAndCanPassRedSignal)
18759  // ignore signal and increment CurrentElement to NextElement
18760  {
18761  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
18762  {
18763  if((NextEntryPos == 0) || (NextEntryPos == 2))
18764  // leading entry point
18765  {
18766  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
18767  {
18768  NextExitPos = 1;
18769  }
18770  else
18771  {
18772  NextExitPos = 3;
18773  }
18774  }
18775  else
18776  {
18777  NextExitPos = 0; // trailing entry point
18778  }
18779  }
18780  else
18781  {
18782  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18783  }
18784  CurrentElement = NextElement;
18785  CurrentEntryPos = NextEntryPos;
18786  CurrentExitPos = NextExitPos;
18787  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
18788  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
18789  }
18790  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
18791  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
18792  {
18793  Utilities->CallLogPop(2084);
18794  return(0);
18795  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
18796  }
18797  }
18798  int LaterStopNumber = 0;
18799  int x = 0;
18800  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
18801 
18802  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
18803  // not red signal next (in fwd direction) so enter loop to calc CumLength
18804  {
18805  x++; // added in v2.4.0 as above
18806  if(x > 5000)
18807  {
18808  Utilities->CallLogPop(2120);
18809  return(-1);
18810  }
18811  if(CurrentEntryPos > 1)
18812  {
18813  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
18814  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
18815  }
18816  else
18817  {
18818  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
18819  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
18820  }
18821  TrackSpeedCount++;
18822 
18823  // added at v2.6.1 to find DistanceToStationStop for trains running early
18824  if(TrainID > -1) //can ignore continuation entries as these don't run early
18825  {
18826  TTrain &Train = TrainVectorAtIdent(52, TrainID);
18827  if(!Train.StationStopCalculated)
18828  {
18829  if(Train.TrainMode == Timetable)
18830  {
18831  bool StopRequired = false;
18832  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
18833  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
18834  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
18835  {
18836  // no need to add in the length of element to CumulativeLength
18837  if(StopRequired)
18838  {
18839  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
18840  Train.StationStopCalculated = true; //don't want to update it with later stops
18841  }
18842  }
18843  }
18844  }
18845  }
18846  // check for train in front, but if on a bridge on other track then ok
18847  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
18848  int TrainOnElement;
18849  if(TE.TrackType != Bridge)
18850  {
18851  TrainOnElement = TE.TrainIDOnElement;
18852  }
18853  else
18854  {
18855  if(CurrentEntryPos > 1)
18856  {
18857  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
18858  }
18859  else
18860  {
18861  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
18862  }
18863  }
18864  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
18865  // train in front before red signal
18866  {
18867  Utilities->CallLogPop(2085);
18868  return(-1);
18869  }
18870  // add to stoptime if required
18871  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
18872  {
18873  double StopTimeDouble;
18874  while(AVPtr->FormatType == PassTime)
18875  {
18876  AVPtr++; // skip past any passes
18877  }
18878  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
18879  (AVPtr->FormatType == TimeTimeLoc)))
18880  // stop due here so calc dwell time & advance Ptr
18881  {
18882  if(AVPtr->FormatType == TimeTimeLoc)
18883  {
18884  LaterStopNumber++;
18885  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18886  if(StopTimeDouble < 0.5)
18887  {
18888  StopTimeDouble = 0.5;
18889  }
18890  // at least 30 secs delay at station
18891  // can't convert a TDateTime to a float directly
18892  LaterStopTime += float(StopTimeDouble);
18893  RecoverableTime += StopTimeDouble - 0.5;
18894  if((LaterStopNumber == 1) && (TrainID > -1))
18895  {
18896  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18897  }
18898  AVPtr++;
18899  }
18900  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
18901  {
18902  if((AVPtr + 1)->FormatType == TimeLoc)
18903  // must be a departure
18904  {
18905  LaterStopNumber++;
18906  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18907  // can't convert a TDateTime to a float directly
18908  if(StopTimeDouble < 0.5)
18909  {
18910  StopTimeDouble = 0.5;
18911  }
18912  // at least 30 secs delay at station
18913  LaterStopTime += float(StopTimeDouble);
18914  RecoverableTime += StopTimeDouble - 0.5;
18915  if((LaterStopNumber == 1) && (TrainID > -1))
18916  {
18917  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18918  }
18919  AVPtr++;
18920  AVPtr++;
18921  }
18922  else // not a departure, does something else at the location so no calculation needed
18923  {
18924  Utilities->CallLogPop(2086);
18925  return(-1);
18926  }
18927  }
18928  }
18929  }
18930  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
18931  if(NextElement == -1) // reached end element, no action needed
18932  {
18933  Utilities->CallLogPop(2077);
18934  return(-1);
18935  }
18936  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
18937  // get NextExitPos
18938  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
18939  {
18940  if((NextEntryPos == 0) || (NextEntryPos == 2))
18941  // leading entry point
18942  {
18943  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
18944  {
18945  NextExitPos = 1;
18946  }
18947  else
18948  {
18949  NextExitPos = 3;
18950  }
18951  }
18952  else
18953  {
18954  NextExitPos = 0; // trailing entry point
18955  }
18956  }
18957  else
18958  {
18959  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18960  }
18961  CurrentElement = NextElement;
18962  CurrentEntryPos = NextEntryPos;
18963  CurrentExitPos = NextExitPos;
18964  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
18965  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
18966  }
18967  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18968  // ok if autosig route after red signal, no action needed
18969  {
18970  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
18971  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
18972  int RouteNumber; // holder for referenced value, not used
18973  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18974  {
18975  Utilities->CallLogPop(2095);
18976  return(-1);
18977  }
18978  }
18979  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
18980  // average line speed/2 (for half distance accelerating and half decelerating.
18981 
18982  float MaxAllowableSpeed;
18983 
18984  if(TrackSpeedCount > 0)
18985  {
18986  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
18987  }
18988  else // shouldn't reach here but include to prevent divide by zero error
18989  {
18990  if(CurrentEntryPos > 1)
18991  {
18992  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
18993  }
18994  else
18995  {
18996  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
18997  }
18998  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
18999  }
19000  float KmPerLocationStop;
19001 
19002  if(LaterStopNumber > 0)
19003  {
19004  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
19005  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
19006  }
19007  else
19008  {
19009  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
19010  // using linear trendline for accel & decel distance at various speeds
19011  // at half braking, speed never < 60 using this
19012  }
19013  if(AvTrackSpeed > MaxAllowableSpeed)
19014  {
19015  AvTrackSpeed = MaxAllowableSpeed;
19016  }
19017  Utilities->CallLogPop(2096);
19018  return(DistanceToRedSignal);
19019 }
19020 
19021 // ---------------------------------------------------------------------------
19022 // end of TTrainController entries
19023 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8337
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17456
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:113
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:347
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14106
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:314
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:313
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:443
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:646
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1695
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:885
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:151
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14129
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:13773
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:335
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:18488
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:754
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15154
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6967
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:94
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:981
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:733
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18462
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:312
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:353
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11000
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:393
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:787
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:294
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7061
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1612
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:891
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1624
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:723
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:119
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:771
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1636
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:68
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:460
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:694
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6125
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:397
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19210
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13057
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:335
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:967
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:379
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:950
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:740
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:308
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:946
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7213
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:355
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:434
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:335
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6239
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:740
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9400
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:330
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:882
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:668
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14885
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:798
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:973
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:619
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:980
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18252
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:740
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10953
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1616
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8309
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:113
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:933
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:690
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14900
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:242
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:931
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6054
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:947
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3438
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:628
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:456
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:418
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9362
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:449
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10625
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:792
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:736
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:315
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:343
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:184
LeadMid
@ LeadMid
Definition: TrainUnit.h:275
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:783
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18725
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13853
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:945
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6260
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:686
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:987
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:229
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:983
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:192
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:802
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:760
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:676
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8615
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15003
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:275
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:839
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15862
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14941
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:275
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:791
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:316
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:408
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:725
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18000
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10561
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:956
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5673
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:463
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:163
TTrain
Definition: TrainUnit.h:279
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:953
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10580
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:155
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11255
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:834
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:227
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:816
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:440
GapJump
@ GapJump
Definition: TrackUnit.h:67
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:974
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:786
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:375
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6005
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8998
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:335
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:141
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1663
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:335
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:329
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:202
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:649
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:333
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14982
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:779
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:298
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:731
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:422
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:95
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:664
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14705
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1379
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5905
End
@ End
Definition: TrackUnit.h:77
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:365
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6841
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8601
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:310
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10354
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:312
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:371
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15065
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:127
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:941
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:581
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:991
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:791
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:186
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:780
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:387
SignalPost
@ SignalPost
Definition: TrackUnit.h:67
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:935
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:426
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1640
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:318
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11624
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:963
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:186
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5334
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:127
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:793
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:717
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:843
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:385
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:76
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:436
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:66
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:789
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:754
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1859
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:440
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:787
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:951
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:96
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:113
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:440
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10633
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:929
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:416
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:954
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9898
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:688
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:335
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:414
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:979
Pass
@ Pass
Definition: TrainUnit.h:52
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:740
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:702
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2962
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11073
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:771
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:966
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:447
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:720
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:824
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7433
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:939
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:662
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:323
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:292
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:972
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:766
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8321
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:975
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:188
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:198
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6697
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:757
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17176
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:428
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14374
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10491
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:600
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:286
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3170
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:986
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:424
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4928
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2483
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:111
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:808
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:612
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:936
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:764
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:959
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:137
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10389
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2894
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6669
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14472
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:874
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13157
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:785
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:808
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:349
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3388
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:932
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:300
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8131
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7410
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2886
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:770
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:443
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:420
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15218
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1479
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:440
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:351
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1623
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:341
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:200
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:196
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:777
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:976
Crossover
@ Crossover
Definition: TrackUnit.h:67
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4538
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:682
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:77
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:938
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:969
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:780
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:155
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17693
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:822
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:17447
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1624
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:422
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10340
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14916
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7697
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17519
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:988
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2100
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:440
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1645
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:928
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:440
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:700
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:288
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:742
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:395
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:754
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:958
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:934
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:335
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:970
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:795
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:712
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:738
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:412
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:363
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2867
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:937
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:750
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2412
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:440
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3417
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7648
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:944
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6199
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14965
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:406
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:253
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13687
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:692
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8650
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14060
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6752
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:748
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10924
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:771
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:109
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17362
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:740
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:282
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:18380
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:401
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:710
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:240
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:746
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:754
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:783
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:440
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:454
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:338
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:262
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3514
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13029
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14484
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:18648
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15241
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:414
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11144
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:721
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:796
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:389
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2297
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18377
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:812
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1610
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9084
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8699
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9591
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:806
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:961
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:130
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8710
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:978
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1697
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:754
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3366
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3066
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:422
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:791
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11041
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1608
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8278
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2930
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:101
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:773
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15087
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:243
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:955
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14451
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:814
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:441
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4494
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:255
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:17807
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9416
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:325
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:373
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11124
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:769
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:99
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8724
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:887
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:404
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1678
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:190
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:132
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:960
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:204
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:762
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:890
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:534
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4951
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:115
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:424
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1614
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1624
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:117
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:304
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:984
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:655
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6499
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:653
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9068
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:778
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:321
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15254
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:758
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:949
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Points
@ Points
Definition: TrackUnit.h:67
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:361
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:772
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13586
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6540
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:359
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:754
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:794
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3362
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:968
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:367
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:458
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16637
Continuation
@ Continuation
Definition: TrackUnit.h:67
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:740
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7406
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:756
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:788
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:432
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9770
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:331
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:17915
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3024
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:883
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:723
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:886
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:982
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9447
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:89
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:438
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:622
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:735
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6442
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:790
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:445
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:276
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:465
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:965
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19040
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:141
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:884
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:957
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10782
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:290
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:261
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:715
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:664
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8241
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:740
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:295
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:709
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:164
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:952
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18150
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18106
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:744
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:103
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:259
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:890
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:302
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:664
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:335
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:782
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:989
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9038
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14398
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7016
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17376
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:381
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5386
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:17205
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9053
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:125
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13036
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:311
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8385
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15047
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:162
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:900
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:357
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:194
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:784
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11472
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:102
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:713
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:754
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:153
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:214
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:804
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:776
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:752
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5035
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:440
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9435
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:948
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:274
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:725
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:217
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14094
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7145
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:410
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:651
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1618
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:121
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:327
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:715
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:942
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:985
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:711
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2988
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:103
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:123
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:377
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:977
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:103
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4661
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:684
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6602
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:369
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:800
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:754
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:940
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6169
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:714
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:18334
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:680
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:768
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:971
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:818
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:666
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:105
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4940
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:399
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:670
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:306
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:746
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:781
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2912
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:91
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:626
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1038
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:962
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:17489
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6470
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3408
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:107
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2500
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:186
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6783
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:309
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:77
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:930
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13045
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:683
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:754
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:160
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:68
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:441
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:964
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:740
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:829
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:225
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5696
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:17231
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:451
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:888
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:160
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:67
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:943
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:257
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:335
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:391
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:67
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:111
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:103